diff --git a/cel-c/BUILD b/cel-c/BUILD index 19227cc..85010b8 100644 --- a/cel-c/BUILD +++ b/cel-c/BUILD @@ -136,6 +136,7 @@ cc_library( deps = [ ":arena", ":assert", + ":attribute", ":config", ":constant", ":operators", @@ -175,6 +176,7 @@ cc_library( ":arena", ":assert", ":ast", + ":attribute", ":config", ":constant_proto", ":operators", @@ -201,6 +203,7 @@ cc_test( ":arena", ":ast", ":ast_proto", + ":attribute", ":config", ":constant", ":duration", @@ -232,6 +235,7 @@ cc_library( ":arena", ":assert", ":ast", + ":attribute", ":config", ":constant_proto_v1alpha1", ":operators", @@ -257,6 +261,7 @@ cc_test( ":arena", ":ast", ":ast_proto_v1alpha1", + ":attribute", ":config", ":constant", ":duration", @@ -325,6 +330,31 @@ cc_library( ], ) +cc_library( + name = "attribute", + srcs = ["attribute.cc"], + hdrs = [ + "attribute.h", + ], + visibility = ["//visibility:public"], + deps = [ + ":assert", + ":config", + ":string_view", + ], +) + +cc_test( + name = "attribute_test", + srcs = ["attribute_test.cc"], + deps = [ + ":attribute", + ":config", + ":string_view", + "@googletest//:gtest_main", + ], +) + cc_library( name = "config", hdrs = ["config.h"], diff --git a/cel-c/ast.cc b/cel-c/ast.cc index b76e987..6c6124f 100644 --- a/cel-c/ast.cc +++ b/cel-c/ast.cc @@ -21,6 +21,7 @@ #include "cel-c/arena.h" #include "cel-c/assert.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant.h" #include "cel-c/operators.h" @@ -333,6 +334,38 @@ struct cel_BlockExpr { cel_Expr* cel_nullable bound; }; +struct cel_AttrQualExpr { + cel_ExprId id; + CEL_NONNULL(cel_Ast*) ast; + CEL_NULLABLE(cel_Expr*) parent; + cel_ExprKind kind; + cel_SourceRange range; + CEL_NULLABLE(const cel_Ref*) ref; + CEL_NULLABLE(cel_Type*) type; + + _cel_ExprLink* cel_nullable left; + _cel_ExprLink* cel_nullable right; + ptrdiff_t index; + + cel_AttributeQualifier value; +}; + +struct cel_AttrExpr { + cel_ExprId id; + CEL_NONNULL(cel_Ast*) ast; + CEL_NULLABLE(cel_Expr*) parent; + cel_ExprKind kind; + cel_SourceRange range; + CEL_NULLABLE(const cel_Ref*) ref; + CEL_NULLABLE(cel_Type*) type; + + _cel_ExprLink* cel_nullable quals_head; + _cel_ExprLink* cel_nullable quals_tail; + size_t quals; + cel_Expr* cel_nullable operand; + bool test_only; +}; + struct _cel_ExprLink { union { struct { @@ -352,6 +385,7 @@ struct _cel_ExprLink { cel_MapEntryExpr map_entry_expr; cel_StructFieldExpr struct_field_expr; cel_BlockBindingExpr block_binding_expr; + cel_AttrQualExpr attr_qual_expr; }; }; @@ -386,6 +420,8 @@ struct cel_Expr { cel_BlockBindingExpr block_binding_expr; cel_BlockIndexExpr block_index_expr; cel_BlockExpr block_expr; + cel_AttrQualExpr attr_qual_expr; + cel_AttrExpr attr_expr; }; }; @@ -2283,3 +2319,181 @@ extern "C" CEL_NULLABLE(cel_BlockBindingExpr*) } return (cel_BlockBindingExpr*)binding; } + +extern "C" cel_AttrQualExpr* cel_nullable +cel_AttrQualExpr_New(cel_Ast* cel_nonnull ast) { + CEL_ASSERT_NOT_NULL(ast); + + cel_AttrQualExpr* expr = (cel_AttrQualExpr*)cel_Arena_Malloc( + ast->arena, sizeof(cel_AttrQualExpr), cel_nullptr); + if (expr != cel_nullptr) { + memset(expr, 0, sizeof(*expr)); + expr->ast = ast; + expr->kind = cel_ExprKind_kAttrQual; + expr->range = cel_SourceRange(-1, -1); + } + return expr; +} + +extern "C" cel_AttrExpr* cel_nullable +cel_AttrQualExpr_Parent(const cel_AttrQualExpr* cel_nonnull expr) { + CEL_ASSERT_NOT_NULL(expr); + + return cel_AttrExpr_DownCast(expr->parent); +} + +extern "C" const cel_AttributeQualifier* cel_nonnull +cel_AttrQualExpr_Value(const cel_AttrQualExpr* cel_nonnull expr) { + CEL_ASSERT_NOT_NULL(expr); + + return &expr->value; +} + +extern "C" cel_AttributeQualifier* cel_nonnull +cel_AttrQualExpr_MutableValue(cel_AttrQualExpr* cel_nonnull expr) { + CEL_ASSERT_NOT_NULL(expr); + + return &expr->value; +} + +extern "C" cel_AttrExpr* cel_nullable cel_AttrExpr_New(CEL_NONNULL(cel_Ast*) + ast) { + CEL_ASSERT_NOT_NULL(ast); + + cel_AttrExpr* expr = (cel_AttrExpr*)cel_Arena_Malloc( + ast->arena, sizeof(cel_AttrExpr), cel_nullptr); + if (expr != cel_nullptr) { + memset(expr, 0, sizeof(*expr)); + expr->ast = ast; + expr->kind = cel_ExprKind_kAttr; + expr->range = cel_SourceRange(-1, -1); + } + return expr; +} + +extern "C" cel_Expr* cel_nullable +cel_AttrExpr_Operand(const cel_AttrExpr* cel_nonnull expr) { + CEL_ASSERT_NOT_NULL(expr); + + return expr->operand; +} + +extern "C" cel_Expr* cel_nullable cel_AttrExpr_SetOperand( + cel_AttrExpr* cel_nonnull expr, cel_Expr* cel_nullable operand) { + CEL_ASSERT_NOT_NULL(expr); + + return _cel_Expr_SetChild(cel_Expr_UpCast(expr), &expr->operand, operand); +} + +extern "C" cel_Expr* cel_nullable +cel_AttrExpr_ReleaseOperand(cel_AttrExpr* cel_nonnull expr) { + CEL_ASSERT_NOT_NULL(expr); + + return _cel_Expr_ReleaseChild(cel_Expr_UpCast(expr), &expr->operand); +} + +extern "C" void cel_AttrExpr_AppendQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier) { + CEL_ASSERT_NOT_NULL(expr); + + _cel_Expr_LinkChild(cel_Expr_UpCast(expr), &expr->quals_head, + &expr->quals_tail, &expr->quals, cel_nullptr, + (_cel_ExprLink*)qualifier); +} + +extern "C" void cel_AttrExpr_PrependQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier) { + CEL_ASSERT_NOT_NULL(expr); + + _cel_Expr_LinkChild(cel_Expr_UpCast(expr), &expr->quals_head, + &expr->quals_tail, &expr->quals, expr->quals_head, + (_cel_ExprLink*)qualifier); +} + +extern "C" void cel_AttrExpr_InsertQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NULLABLE(cel_AttrQualExpr*) + before, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier) { + CEL_ASSERT_NOT_NULL(expr); + + _cel_Expr_LinkChild(cel_Expr_UpCast(expr), &expr->quals_head, + &expr->quals_tail, &expr->quals, (_cel_ExprLink*)before, + (_cel_ExprLink*)qualifier); +} + +extern "C" CEL_NONNULL(cel_AttrQualExpr*) + cel_AttrExpr_ReleaseQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) qualifier) { + CEL_ASSERT_NOT_NULL(expr); + + _cel_Expr_UnlinkChild(cel_Expr_UpCast(expr), &expr->quals_head, + &expr->quals_tail, &expr->quals, + (_cel_ExprLink*)qualifier); + return qualifier; +} + +extern "C" CEL_NONNULL(cel_AttrQualExpr*) + cel_AttrExpr_Qualifier(CEL_NONNULL(const cel_AttrExpr*) expr, + size_t index) { + CEL_ASSERT_NOT_NULL(expr); + CEL_ASSERT_LT(index, expr->quals); + + CEL_NULLABLE(_cel_ExprLink*) arg = expr->quals_head; + for (; index > 0; --index) { + CEL_ASSERT_NOT_NULL(arg); + arg = arg->right; + } + CEL_ASSERT_NOT_NULL(arg); + CEL_ASSERT(arg->parent == cel_Expr_UpCast(expr)); + return (cel_AttrQualExpr*)arg; +} + +extern "C" size_t cel_AttrExpr_Qualifiers(CEL_NONNULL(const cel_AttrExpr*) expr, + CEL_NULLABLE(cel_AttrQualExpr*) * + cel_nullable head, + CEL_NULLABLE(cel_AttrQualExpr*) * + cel_nullable tail) { + CEL_ASSERT_NOT_NULL(expr); + + if (head != cel_nullptr) { + *head = (cel_AttrQualExpr*)expr->quals_head; + } + if (tail != cel_nullptr) { + *tail = (cel_AttrQualExpr*)expr->quals_tail; + } + return expr->quals; +} + +extern "C" CEL_NULLABLE(cel_AttrQualExpr*) + cel_AttrExpr_PrevQualifier(CEL_NULLABLE(const cel_AttrQualExpr*) + qualifier) { + if (qualifier != cel_nullptr) { + qualifier = (cel_AttrQualExpr*)qualifier->left; + } + return (cel_AttrQualExpr*)qualifier; +} + +extern "C" CEL_NULLABLE(cel_AttrQualExpr*) + cel_AttrExpr_NextQualifier(CEL_NULLABLE(const cel_AttrQualExpr*) + qualifier) { + if (qualifier != cel_nullptr) { + qualifier = (cel_AttrQualExpr*)qualifier->right; + } + return (cel_AttrQualExpr*)qualifier; +} + +extern "C" bool cel_AttrExpr_TestOnly(CEL_NONNULL(const cel_AttrExpr*) expr) { + CEL_ASSERT_NOT_NULL(expr); + + return expr->test_only; +} + +extern "C" void cel_AttrExpr_SetTestOnly(CEL_NONNULL(cel_AttrExpr*) expr, + bool test_only) { + CEL_ASSERT_NOT_NULL(expr); + + expr->test_only = test_only; +} diff --git a/cel-c/ast.h b/cel-c/ast.h index 22da4e0..5efba34 100644 --- a/cel-c/ast.h +++ b/cel-c/ast.h @@ -21,6 +21,7 @@ #include "cel-c/arena.h" #include "cel-c/assert.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant.h" #include "cel-c/operators.h" @@ -52,6 +53,8 @@ typedef enum CEL_ATTRIBUTE_OPEN_ENUM { cel_ExprKind_kBlock, cel_ExprKind_kBlockBinding, cel_ExprKind_kBlockIndex, + cel_ExprKind_kAttrQual, + cel_ExprKind_kAttr, } cel_ExprKind; typedef int64_t cel_ExprId; @@ -79,6 +82,8 @@ typedef struct cel_BindExpr cel_BindExpr; typedef struct cel_BlockIndexExpr cel_BlockIndexExpr; typedef struct cel_BlockBindingExpr cel_BlockBindingExpr; typedef struct cel_BlockExpr cel_BlockExpr; +typedef struct cel_AttrQualExpr cel_AttrQualExpr; +typedef struct cel_AttrExpr cel_AttrExpr; // cel_Ast @@ -1726,6 +1731,165 @@ static CEL_INLINE CEL_NULLABILITY_UNKNOWN(const cel_BlockExpr*) return (CEL_NULLABILITY_UNKNOWN(const cel_BlockExpr*))expr; } +// cel_AttrQualExpr + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_AttrQualExpr* cel_nullable +cel_AttrQualExpr_New(cel_Ast* cel_nonnull ast); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_AttrExpr* cel_nullable +cel_AttrQualExpr_Parent(const cel_AttrQualExpr* cel_nonnull expr); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN const cel_AttributeQualifier* cel_nonnull +cel_AttrQualExpr_Value(const cel_AttrQualExpr* cel_nonnull expr); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_AttributeQualifier* cel_nonnull +cel_AttrQualExpr_MutableValue(cel_AttrQualExpr* cel_nonnull expr); + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(cel_Expr*) + cel_AttrQualExpr_MutableUpCast(CEL_NULLABILITY_UNKNOWN(cel_AttrQualExpr*) + expr) { + return (CEL_NULLABILITY_UNKNOWN(cel_Expr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(const cel_Expr*) + cel_AttrQualExpr_ConstUpCast( + CEL_NULLABILITY_UNKNOWN(const cel_AttrQualExpr*) expr) { + return (CEL_NULLABILITY_UNKNOWN(const cel_Expr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(cel_AttrQualExpr*) + cel_AttrQualExpr_MutableDownCast(CEL_NULLABILITY_UNKNOWN(cel_Expr*) expr) { + CEL_ASSERT(expr == cel_nullptr || + cel_Expr_Kind(expr) == cel_ExprKind_kAttrQual); + + return (CEL_NULLABILITY_UNKNOWN(cel_AttrQualExpr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(const cel_AttrQualExpr*) + cel_AttrQualExpr_ConstDownCast(CEL_NULLABILITY_UNKNOWN(const cel_Expr*) + expr) { + CEL_ASSERT(expr == cel_nullptr || + cel_Expr_Kind(expr) == cel_ExprKind_kAttrQual); + + return (CEL_NULLABILITY_UNKNOWN(const cel_AttrQualExpr*))expr; +} + +// cel_AttrExpr + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_AttrExpr* cel_nullable cel_AttrExpr_New(CEL_NONNULL(cel_Ast*) + ast); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_Expr* cel_nullable +cel_AttrExpr_Operand(const cel_AttrExpr* cel_nonnull expr); + +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_Expr* cel_nullable cel_AttrExpr_SetOperand( + cel_AttrExpr* cel_nonnull expr, cel_Expr* cel_nullable operand); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN cel_Expr* cel_nullable +cel_AttrExpr_ReleaseOperand(cel_AttrExpr* cel_nonnull expr); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN void cel_AttrExpr_AppendQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN void cel_AttrExpr_PrependQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN void cel_AttrExpr_InsertQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NULLABLE(cel_AttrQualExpr*) + before, + CEL_NONNULL(cel_AttrQualExpr*) + qualifier); + +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN CEL_NONNULL(cel_AttrQualExpr*) + cel_AttrExpr_ReleaseQualifier(CEL_NONNULL(cel_AttrExpr*) expr, + CEL_NONNULL(cel_AttrQualExpr*) qualifier); + +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN CEL_NONNULL(cel_AttrQualExpr*) + cel_AttrExpr_Qualifier(CEL_NONNULL(const cel_AttrExpr*) expr, size_t index); + +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN size_t cel_AttrExpr_Qualifiers(CEL_NONNULL(const cel_AttrExpr*) expr, + CEL_NULLABLE(cel_AttrQualExpr*) * + cel_nullable head, + CEL_NULLABLE(cel_AttrQualExpr*) * + cel_nullable tail); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN CEL_NULLABLE(cel_AttrQualExpr*) + cel_AttrExpr_PrevQualifier(CEL_NULLABLE(const cel_AttrQualExpr*) qualifier); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN CEL_NULLABLE(cel_AttrQualExpr*) + cel_AttrExpr_NextQualifier(CEL_NULLABLE(const cel_AttrQualExpr*) qualifier); + +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN bool cel_AttrExpr_TestOnly(CEL_NONNULL(const cel_AttrExpr*) expr); + +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN void cel_AttrExpr_SetTestOnly(CEL_NONNULL(cel_AttrExpr*) expr, + bool test_only); + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(cel_Expr*) + cel_AttrExpr_MutableUpCast(CEL_NULLABILITY_UNKNOWN(cel_AttrExpr*) expr) { + return (CEL_NULLABILITY_UNKNOWN(cel_Expr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(const cel_Expr*) + cel_AttrExpr_ConstUpCast(CEL_NULLABILITY_UNKNOWN(const cel_AttrExpr*) + expr) { + return (CEL_NULLABILITY_UNKNOWN(const cel_Expr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(cel_AttrExpr*) + cel_AttrExpr_MutableDownCast(CEL_NULLABILITY_UNKNOWN(cel_Expr*) expr) { + CEL_ASSERT(expr == cel_nullptr || cel_Expr_Kind(expr) == cel_ExprKind_kAttr); + + return (CEL_NULLABILITY_UNKNOWN(cel_AttrExpr*))expr; +} + +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE CEL_NULLABILITY_UNKNOWN(const cel_AttrExpr*) + cel_AttrExpr_ConstDownCast(CEL_NULLABILITY_UNKNOWN(const cel_Expr*) expr) { + CEL_ASSERT(expr == cel_nullptr || cel_Expr_Kind(expr) == cel_ExprKind_kAttr); + + return (CEL_NULLABILITY_UNKNOWN(const cel_AttrExpr*))expr; +} + CEL_END_DECLS #ifndef __cplusplus @@ -1770,7 +1934,11 @@ CEL_END_DECLS cel_BlockIndexExpr*: cel_BlockIndexExpr_MutableUpCast, \ const cel_BlockIndexExpr*: cel_BlockIndexExpr_ConstUpCast, \ cel_BlockExpr*: cel_BlockExpr_MutableUpCast, \ - const cel_BlockExpr*: cel_BlockExpr_ConstUpCast)(expr)) + const cel_BlockExpr*: cel_BlockExpr_ConstUpCast, \ + cel_AttrQualExpr*: cel_AttrQualExpr_MutableUpCast, \ + const cel_AttrQualExpr*: cel_AttrQualExpr_ConstUpCast, \ + cel_AttrExpr*: cel_AttrExpr_MutableUpCast, \ + const cel_AttrExpr*: cel_AttrExpr_ConstUpCast)(expr)) #define cel_UnspecifiedExpr_DownCast(expr) \ (_Generic((expr), \ cel_Expr*: cel_UnspecifiedExpr_MutableDownCast, \ @@ -1847,6 +2015,14 @@ CEL_END_DECLS (_Generic((expr), \ cel_Expr*: cel_BlockExpr_MutableDownCast, \ const cel_Expr*: cel_BlockExpr_ConstDownCast)(expr)) +#define cel_AttrQualExpr_DownCast(expr) \ + (_Generic((expr), \ + cel_Expr*: cel_AttrQualExpr_MutableDownCast, \ + const cel_Expr*: cel_AttrQualExpr_ConstDownCast)(expr)) +#define cel_AttrExpr_DownCast(expr) \ + (_Generic((expr), \ + cel_Expr*: cel_AttrExpr_MutableDownCast, \ + const cel_Expr*: cel_AttrExpr_ConstDownCast)(expr)) #else CEL_ATTRIBUTE_NODISCARD CEL_INLINE const cel_Expr* cel_nullability_unknown @@ -2049,6 +2225,26 @@ cel_Expr_UpCast(cel_BlockExpr* cel_nullability_unknown expr) { return cel_BlockExpr_MutableUpCast(expr); } CEL_ATTRIBUTE_NODISCARD +CEL_INLINE const cel_Expr* cel_nullability_unknown +cel_Expr_UpCast(const cel_AttrQualExpr* cel_nullability_unknown expr) { + return cel_AttrQualExpr_ConstUpCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE cel_Expr* cel_nullability_unknown +cel_Expr_UpCast(cel_AttrQualExpr* cel_nullability_unknown expr) { + return cel_AttrQualExpr_MutableUpCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE const cel_Expr* cel_nullability_unknown +cel_Expr_UpCast(const cel_AttrExpr* cel_nullability_unknown expr) { + return cel_AttrExpr_ConstUpCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE cel_Expr* cel_nullability_unknown +cel_Expr_UpCast(cel_AttrExpr* cel_nullability_unknown expr) { + return cel_AttrExpr_MutableUpCast(expr); +} +CEL_ATTRIBUTE_NODISCARD CEL_INLINE const cel_UnspecifiedExpr* cel_nullability_unknown cel_UnspecifiedExpr_DownCast(const cel_Expr* cel_nullability_unknown expr) { return cel_UnspecifiedExpr_ConstDownCast(expr); @@ -2248,6 +2444,26 @@ CEL_INLINE cel_BlockExpr* cel_nullability_unknown cel_BlockExpr_DownCast(cel_Expr* cel_nullability_unknown expr) { return cel_BlockExpr_MutableDownCast(expr); } +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE const cel_AttrQualExpr* cel_nullability_unknown +cel_AttrQualExpr_DownCast(const cel_Expr* cel_nullability_unknown expr) { + return cel_AttrQualExpr_ConstDownCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE cel_AttrQualExpr* cel_nullability_unknown +cel_AttrQualExpr_DownCast(cel_Expr* cel_nullability_unknown expr) { + return cel_AttrQualExpr_MutableDownCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE const cel_AttrExpr* cel_nullability_unknown +cel_AttrExpr_DownCast(const cel_Expr* cel_nullability_unknown expr) { + return cel_AttrExpr_ConstDownCast(expr); +} +CEL_ATTRIBUTE_NODISCARD +CEL_INLINE cel_AttrExpr* cel_nullability_unknown +cel_AttrExpr_DownCast(cel_Expr* cel_nullability_unknown expr) { + return cel_AttrExpr_MutableDownCast(expr); +} #endif #endif // THIRD_PARTY_CEL_C_AST_H_ diff --git a/cel-c/ast_proto.cc b/cel-c/ast_proto.cc index 941d57e..fab8a45 100644 --- a/cel-c/ast_proto.cc +++ b/cel-c/ast_proto.cc @@ -24,6 +24,7 @@ #include "cel-c/arena.h" #include "cel-c/assert.h" #include "cel-c/ast.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant_proto.h" #include "cel-c/internal/charconv.h" @@ -46,6 +47,113 @@ typedef struct { _cel_jmp_buf jmp; } _cel_AstFromProtoState; +static bool _cel_AstFromProto_IsAttributeOrHasField( + CEL_NONNULL(const cel_expr_Expr_Call*) expr) { + size_t expected_num_args; + if (cel_StringView_Equals(cel_expr_Expr_Call_function(expr), + cel_StringView_From("cel.@attribute"))) { + expected_num_args = 3; + } else if (cel_StringView_Equals(cel_expr_Expr_Call_function(expr), + cel_StringView_From("cel.@hasField"))) { + expected_num_args = 2; + } else { + return false; + } + if (cel_expr_Expr_Call_has_target(expr)) { + return false; + } + size_t num_args = 0; + const cel_expr_Expr* const* args = + cel_expr_Expr_Call_args(expr, &num_args); + if (num_args != expected_num_args || args[0] == nullptr || + args[1] == nullptr || (expected_num_args == 3 && args[2] == nullptr)) { + return false; + } + // Check that the second argument is a list of lists where each list starts + // with at least one constant or two. + if (!cel_expr_Expr_has_list_expr(args[1])) { + return false; + } + const cel_expr_Expr_CreateList* list_expr = + cel_expr_Expr_list_expr(args[1]); + size_t optional_indices = 0; + cel_expr_Expr_CreateList_optional_indices(list_expr, + &optional_indices); + if (optional_indices != 0) { + return false; + } + size_t num_elems = 0; + const cel_expr_Expr* const* elems = + cel_expr_Expr_CreateList_elements(list_expr, &num_elems); + if (num_elems == 0) { + return false; + } + for (size_t i = 0; i < num_elems; ++i) { + const cel_expr_Expr* elem = elems[i]; + if (elem == nullptr) { + return false; + } + if (!cel_expr_Expr_has_list_expr(elem)) { + return false; + } + const cel_expr_Expr_CreateList* qual_expr = + cel_expr_Expr_list_expr(elem); + optional_indices = 0; + cel_expr_Expr_CreateList_optional_indices(qual_expr, + &optional_indices); + if (optional_indices != 0) { + return false; + } + size_t num_qual_elems = 0; + const cel_expr_Expr* const* qual_elems = + cel_expr_Expr_CreateList_elements(qual_expr, &num_qual_elems); + if (num_qual_elems == 0) { + return false; + } + if (num_qual_elems == 1) { + // Should be `[]`. With the contant being the first element in + // the list. + if (qual_elems[0] == nullptr || + !cel_expr_Expr_has_const_expr(qual_elems[0])) { + return false; + } + switch (cel_expr_Constant_constant_kind_case( + cel_expr_Expr_const_expr(qual_elems[0]))) { + case cel_expr_Constant_constant_kind_bool_value: + [[fallthrough]]; + case cel_expr_Constant_constant_kind_int64_value: + [[fallthrough]]; + case cel_expr_Constant_constant_kind_uint64_value: + [[fallthrough]]; + case cel_expr_Constant_constant_kind_string_value: + break; + default: + return false; + } + } else { + // Should be `.`. With the field name being the second element in + // the list as a string constant. The first element is a field number, we + // validate but ignore it. + if (qual_elems[0] == nullptr || qual_elems[1] == nullptr || + !cel_expr_Expr_has_const_expr(qual_elems[0]) || + !cel_expr_Expr_has_const_expr(qual_elems[1])) { + return false; + } + if (cel_expr_Constant_constant_kind_case( + cel_expr_Expr_const_expr(qual_elems[0])) != + cel_expr_Constant_constant_kind_int64_value) { + return false; + } + if (cel_expr_Constant_constant_kind_case( + cel_expr_Expr_const_expr(qual_elems[1])) != + cel_expr_Constant_constant_kind_string_value) { + return false; + } + } + } + return true; +} + static bool _cel_AstFromProto_IsBlockIndex( CEL_NONNULL(const cel_expr_Expr_Ident*) expr, int64_t* cel_nonnull index) { @@ -438,6 +546,82 @@ static CEL_NONNULL(cel_Expr*) cel_BlockExpr_SetBound(expr, _cel_AstFromProto_Expr(state, in_args[1])); return cel_Expr_UpCast(expr); } + if (_cel_AstFromProto_IsAttributeOrHasField(in)) { + _cel_AstFromProto_CheckId(state, id); + cel_AttrExpr* expr = cel_AttrExpr_New(state->ast); + if (CEL_UNLIKELY(expr == cel_nullptr)) { + cel_OutOfMemoryStatus(state->status); + _cel_longjmp(state->jmp); + } + cel_Expr_SetId(cel_Expr_UpCast(expr), id); + _cel_AstFromProto_UpdatePosition(state, cel_Expr_UpCast(expr)); + _cel_AstFromProto_SetType(state, cel_Expr_UpCast(expr)); + _cel_AstFromProto_SetRef(state, cel_Expr_UpCast(expr)); + cel_AttrExpr_SetOperand(expr, _cel_AstFromProto_Expr(state, in_args[0])); + size_t in_quals_len = 0; + const cel_expr_Expr* const* in_quals = + cel_expr_Expr_CreateList_elements( + cel_expr_Expr_list_expr(in_args[1]), &in_quals_len); + for (size_t i = 0; i < in_quals_len; ++i) { + size_t in_qual_len = 0; + const cel_expr_Expr* const* in_qual = + cel_expr_Expr_CreateList_elements( + cel_expr_Expr_list_expr(in_quals[i]), &in_qual_len); + CEL_ASSERT_GE(in_qual_len, size_t{1}); + cel_AttrQualExpr* qual_expr = cel_AttrQualExpr_New(state->ast); + if (CEL_UNLIKELY(qual_expr == cel_nullptr)) { + cel_OutOfMemoryStatus(state->status); + _cel_longjmp(state->jmp); + } + int64_t qual_expr_id; + if (in_qual_len == 1) { + qual_expr_id = cel_expr_Expr_id(in_qual[0]); + switch (cel_expr_Constant_constant_kind_case( + cel_expr_Expr_const_expr(in_qual[0]))) { + case cel_expr_Constant_constant_kind_bool_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_BoolAttributeQualifier(cel_expr_Constant_bool_value( + cel_expr_Expr_const_expr(in_qual[0]))); + break; + case cel_expr_Constant_constant_kind_int64_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_IntAttributeQualifier(cel_expr_Constant_int64_value( + cel_expr_Expr_const_expr(in_qual[0]))); + break; + case cel_expr_Constant_constant_kind_uint64_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_UintAttributeQualifier( + cel_expr_Constant_uint64_value( + cel_expr_Expr_const_expr(in_qual[0]))); + break; + case cel_expr_Constant_constant_kind_string_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_StringAttributeQualifier(_cel_AstFromProto_StrDup( + state, cel_expr_Constant_string_value( + cel_expr_Expr_const_expr(in_qual[0])))); + break; + default: + CEL_UNREACHABLE(); + } + } else { + qual_expr_id = cel_expr_Expr_id(in_qual[1]); + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_StringAttributeQualifier(_cel_AstFromProto_StrDup( + state, cel_expr_Constant_string_value( + cel_expr_Expr_const_expr(in_qual[1])))); + } + _cel_AstFromProto_CheckId(state, qual_expr_id); + cel_Expr_SetId(cel_Expr_UpCast(qual_expr), qual_expr_id); + _cel_AstFromProto_UpdatePosition(state, cel_Expr_UpCast(qual_expr)); + _cel_AstFromProto_SetType(state, cel_Expr_UpCast(qual_expr)); + _cel_AstFromProto_SetRef(state, cel_Expr_UpCast(qual_expr)); + cel_AttrExpr_AppendQualifier(expr, qual_expr); + } + cel_AttrExpr_SetTestOnly( + expr, cel_StringView_Equals(cel_expr_Expr_Call_function(in), + cel_StringView_From("cel.@hasField"))); + return cel_Expr_UpCast(expr); + } cel_StringView function = cel_expr_Expr_Call_function(in); if (!cel_StringView_Empty(function) && !cel_expr_Expr_Call_has_target(in)) { diff --git a/cel-c/ast_proto_test.cc b/cel-c/ast_proto_test.cc index 808085e..967df9b 100644 --- a/cel-c/ast_proto_test.cc +++ b/cel-c/ast_proto_test.cc @@ -27,6 +27,7 @@ #include "cel-c/alloc.h" #include "cel-c/arena.h" #include "cel-c/ast.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant.h" #include "cel-c/duration.h" @@ -1186,4 +1187,298 @@ TEST_F(AstFromProtoTest, Block) { cel_StringView_From("baz"))); } +TEST_F(AstFromProtoTest, Attribute) { + AstPtr ast( + cel_Ast_FromProto(ParseTextProto(R"pb( + source_info: { + positions { key: 1 value: 1 } + positions { key: 2 value: 2 } + positions { key: 3 value: 3 } + positions { key: 4 value: 4 } + positions { key: 5 value: 5 } + positions { key: 6 value: 6 } + positions { key: 7 value: 7 } + positions { key: 8 value: 8 } + positions { key: 9 value: 9 } + positions { key: 10 value: 10 } + positions { key: 11 value: 11 } + positions { key: 12 value: 12 } + positions { key: 13 value: 13 } + positions { key: 14 value: 14 } + } + expr: { + id: 1 + call_expr: { + function: "cel.@attribute" + args { + id: 2 + ident_expr: { name: "baz" } + } + args { + id: 3 + list_expr: { + elements { + id: 4 + list_expr: { + elements { + id: 5 + const_expr: { int64_value: 1 } + } + elements { + id: 6 + const_expr: { string_value: "foo" } + } + } + } + elements { + id: 7 + list_expr: { + elements { + id: 8 + const_expr: { bool_value: true } + } + } + } + elements { + id: 9 + list_expr: { + elements { + id: 10 + const_expr: { int64_value: 2 } + } + } + } + elements { + id: 11 + list_expr: { + elements { + id: 12 + const_expr: { uint64_value: 3 } + } + } + } + elements { + id: 13 + list_expr: { + elements { + id: 14 + const_expr: { string_value: "bar" } + } + } + } + } + } + args { + id: 15 + const_expr: { int64_value: 0 } + } + } + } + )pb"), + arena(), status())); + ASSERT_THAT(ast, NotNull()); + ASSERT_TRUE(cel_Status_Ok(status())); + EXPECT_EQ(cel_Ast_MaxId(ast.get()), 14); + + cel_Expr* expr = cel_Ast_Expr(ast.get()); + ASSERT_THAT(expr, NotNull()); + ASSERT_EQ(cel_Expr_Kind(expr), cel_ExprKind_kAttr); + EXPECT_EQ(cel_Expr_SourcePosition(expr), 1); + + cel_AttrExpr* attr_expr = cel_AttrExpr_DownCast(expr); + ASSERT_THAT(attr_expr, NotNull()); + EXPECT_FALSE(cel_AttrExpr_TestOnly(attr_expr)); + + cel_Expr* operand = cel_AttrExpr_Operand(attr_expr); + ASSERT_THAT(operand, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(operand), 2); + ASSERT_EQ(cel_Expr_Kind(operand), cel_ExprKind_kIdent); + + ASSERT_EQ(cel_AttrExpr_Qualifiers(attr_expr, nullptr, nullptr), 5); + + cel_AttrQualExpr* qual0 = cel_AttrExpr_Qualifier(attr_expr, 0); + ASSERT_THAT(qual0, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual0)), 6); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual0)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual0)), + cel_StringView_From("foo"))); + + cel_AttrQualExpr* qual1 = cel_AttrExpr_Qualifier(attr_expr, 1); + ASSERT_THAT(qual1, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual1)), 8); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual1)), + cel_AttributeQualifierKind_kBool); + EXPECT_TRUE(cel_AttributeQualifier_GetBool(cel_AttrQualExpr_Value(qual1))); + + cel_AttrQualExpr* qual2 = cel_AttrExpr_Qualifier(attr_expr, 2); + ASSERT_THAT(qual2, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual2)), 10); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual2)), + cel_AttributeQualifierKind_kInt); + EXPECT_EQ(cel_AttributeQualifier_GetInt(cel_AttrQualExpr_Value(qual2)), 2); + + cel_AttrQualExpr* qual3 = cel_AttrExpr_Qualifier(attr_expr, 3); + ASSERT_THAT(qual3, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual3)), 12); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual3)), + cel_AttributeQualifierKind_kUint); + EXPECT_EQ(cel_AttributeQualifier_GetUint(cel_AttrQualExpr_Value(qual3)), 3); + + cel_AttrQualExpr* qual4 = cel_AttrExpr_Qualifier(attr_expr, 4); + ASSERT_THAT(qual4, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual4)), 14); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual4)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual4)), + cel_StringView_From("bar"))); +} + +TEST_F(AstFromProtoTest, HasField) { + AstPtr ast( + cel_Ast_FromProto(ParseTextProto(R"pb( + source_info: { + positions { key: 1 value: 1 } + positions { key: 2 value: 2 } + positions { key: 3 value: 3 } + positions { key: 4 value: 4 } + positions { key: 5 value: 5 } + positions { key: 6 value: 6 } + positions { key: 7 value: 7 } + positions { key: 8 value: 8 } + positions { key: 9 value: 9 } + positions { key: 10 value: 10 } + positions { key: 11 value: 11 } + positions { key: 12 value: 12 } + positions { key: 13 value: 13 } + positions { key: 14 value: 14 } + } + expr: { + id: 1 + call_expr: { + function: "cel.@hasField" + args { + id: 2 + ident_expr: { name: "baz" } + } + args { + id: 3 + list_expr: { + elements { + id: 4 + list_expr: { + elements { + id: 5 + const_expr: { int64_value: 1 } + } + elements { + id: 6 + const_expr: { string_value: "foo" } + } + } + } + elements { + id: 7 + list_expr: { + elements { + id: 8 + const_expr: { bool_value: true } + } + } + } + elements { + id: 9 + list_expr: { + elements { + id: 10 + const_expr: { int64_value: 2 } + } + } + } + elements { + id: 11 + list_expr: { + elements { + id: 12 + const_expr: { uint64_value: 3 } + } + } + } + elements { + id: 13 + list_expr: { + elements { + id: 14 + const_expr: { string_value: "bar" } + } + } + } + } + } + } + } + )pb"), + arena(), status())); + ASSERT_THAT(ast, NotNull()); + ASSERT_TRUE(cel_Status_Ok(status())); + EXPECT_EQ(cel_Ast_MaxId(ast.get()), 14); + + cel_Expr* expr = cel_Ast_Expr(ast.get()); + ASSERT_THAT(expr, NotNull()); + ASSERT_EQ(cel_Expr_Kind(expr), cel_ExprKind_kAttr); + EXPECT_EQ(cel_Expr_SourcePosition(expr), 1); + + cel_AttrExpr* attr_expr = cel_AttrExpr_DownCast(expr); + ASSERT_THAT(attr_expr, NotNull()); + EXPECT_TRUE(cel_AttrExpr_TestOnly(attr_expr)); + + cel_Expr* operand = cel_AttrExpr_Operand(attr_expr); + ASSERT_THAT(operand, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(operand), 2); + ASSERT_EQ(cel_Expr_Kind(operand), cel_ExprKind_kIdent); + + ASSERT_EQ(cel_AttrExpr_Qualifiers(attr_expr, nullptr, nullptr), 5); + + cel_AttrQualExpr* qual0 = cel_AttrExpr_Qualifier(attr_expr, 0); + ASSERT_THAT(qual0, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual0)), 6); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual0)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual0)), + cel_StringView_From("foo"))); + + cel_AttrQualExpr* qual1 = cel_AttrExpr_Qualifier(attr_expr, 1); + ASSERT_THAT(qual1, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual1)), 8); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual1)), + cel_AttributeQualifierKind_kBool); + EXPECT_TRUE(cel_AttributeQualifier_GetBool(cel_AttrQualExpr_Value(qual1))); + + cel_AttrQualExpr* qual2 = cel_AttrExpr_Qualifier(attr_expr, 2); + ASSERT_THAT(qual2, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual2)), 10); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual2)), + cel_AttributeQualifierKind_kInt); + EXPECT_EQ(cel_AttributeQualifier_GetInt(cel_AttrQualExpr_Value(qual2)), 2); + + cel_AttrQualExpr* qual3 = cel_AttrExpr_Qualifier(attr_expr, 3); + ASSERT_THAT(qual3, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual3)), 12); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual3)), + cel_AttributeQualifierKind_kUint); + EXPECT_EQ(cel_AttributeQualifier_GetUint(cel_AttrQualExpr_Value(qual3)), 3); + + cel_AttrQualExpr* qual4 = cel_AttrExpr_Qualifier(attr_expr, 4); + ASSERT_THAT(qual4, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual4)), 14); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual4)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual4)), + cel_StringView_From("bar"))); +} + } // namespace diff --git a/cel-c/ast_proto_v1alpha1.cc b/cel-c/ast_proto_v1alpha1.cc index 84c2cde..f190d19 100644 --- a/cel-c/ast_proto_v1alpha1.cc +++ b/cel-c/ast_proto_v1alpha1.cc @@ -24,6 +24,7 @@ #include "cel-c/arena.h" #include "cel-c/assert.h" #include "cel-c/ast.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant_proto_v1alpha1.h" #include "cel-c/internal/charconv.h" @@ -46,6 +47,115 @@ typedef struct { _cel_jmp_buf jmp; } _cel_AstFromProtoV1Alpha1State; +static bool _cel_AstFromProtoV1Alpha1_IsAttributeOrHasField( + CEL_NONNULL(const google_api_expr_v1alpha1_Expr_Call*) expr) { + size_t expected_num_args; + if (cel_StringView_Equals(google_api_expr_v1alpha1_Expr_Call_function(expr), + cel_StringView_From("cel.@attribute"))) { + expected_num_args = 3; + } else if (cel_StringView_Equals( + google_api_expr_v1alpha1_Expr_Call_function(expr), + cel_StringView_From("cel.@hasField"))) { + expected_num_args = 2; + } else { + return false; + } + if (google_api_expr_v1alpha1_Expr_Call_has_target(expr)) { + return false; + } + size_t num_args = 0; + const google_api_expr_v1alpha1_Expr* const* args = + google_api_expr_v1alpha1_Expr_Call_args(expr, &num_args); + if (num_args != expected_num_args || args[0] == nullptr || + args[1] == nullptr || (expected_num_args == 3 && args[2] == nullptr)) { + return false; + } + // Check that the second argument is a list of lists where each list starts + // with at least one constant or two. + if (!google_api_expr_v1alpha1_Expr_has_list_expr(args[1])) { + return false; + } + const google_api_expr_v1alpha1_Expr_CreateList* list_expr = + google_api_expr_v1alpha1_Expr_list_expr(args[1]); + size_t optional_indices = 0; + google_api_expr_v1alpha1_Expr_CreateList_optional_indices(list_expr, + &optional_indices); + if (optional_indices != 0) { + return false; + } + size_t num_elems = 0; + const google_api_expr_v1alpha1_Expr* const* elems = + google_api_expr_v1alpha1_Expr_CreateList_elements(list_expr, &num_elems); + if (num_elems == 0) { + return false; + } + for (size_t i = 0; i < num_elems; ++i) { + const google_api_expr_v1alpha1_Expr* elem = elems[i]; + if (elem == nullptr) { + return false; + } + if (!google_api_expr_v1alpha1_Expr_has_list_expr(elem)) { + return false; + } + const google_api_expr_v1alpha1_Expr_CreateList* qual_expr = + google_api_expr_v1alpha1_Expr_list_expr(elem); + optional_indices = 0; + google_api_expr_v1alpha1_Expr_CreateList_optional_indices( + qual_expr, &optional_indices); + if (optional_indices != 0) { + return false; + } + size_t num_qual_elems = 0; + const google_api_expr_v1alpha1_Expr* const* qual_elems = + google_api_expr_v1alpha1_Expr_CreateList_elements(qual_expr, + &num_qual_elems); + if (num_qual_elems == 0) { + return false; + } + if (num_qual_elems == 1) { + // Should be `[]`. With the contant being the first element in + // the list. + if (qual_elems[0] == nullptr || + !google_api_expr_v1alpha1_Expr_has_const_expr(qual_elems[0])) { + return false; + } + switch (google_api_expr_v1alpha1_Constant_constant_kind_case( + google_api_expr_v1alpha1_Expr_const_expr(qual_elems[0]))) { + case google_api_expr_v1alpha1_Constant_constant_kind_bool_value: + [[fallthrough]]; + case google_api_expr_v1alpha1_Constant_constant_kind_int64_value: + [[fallthrough]]; + case google_api_expr_v1alpha1_Constant_constant_kind_uint64_value: + [[fallthrough]]; + case google_api_expr_v1alpha1_Constant_constant_kind_string_value: + break; + default: + return false; + } + } else { + // Should be `.`. With the field name being the second element in + // the list as a string constant. The first element is a field number, we + // validate but ignore it. + if (qual_elems[0] == nullptr || qual_elems[1] == nullptr || + !google_api_expr_v1alpha1_Expr_has_const_expr(qual_elems[0]) || + !google_api_expr_v1alpha1_Expr_has_const_expr(qual_elems[1])) { + return false; + } + if (google_api_expr_v1alpha1_Constant_constant_kind_case( + google_api_expr_v1alpha1_Expr_const_expr(qual_elems[0])) != + google_api_expr_v1alpha1_Constant_constant_kind_int64_value) { + return false; + } + if (google_api_expr_v1alpha1_Constant_constant_kind_case( + google_api_expr_v1alpha1_Expr_const_expr(qual_elems[1])) != + google_api_expr_v1alpha1_Constant_constant_kind_string_value) { + return false; + } + } + } + return true; +} + static bool _cel_AstFromProtoV1Alpha1_IsBlockIndex( CEL_NONNULL(const google_api_expr_v1alpha1_Expr_Ident*) expr, int64_t* cel_nonnull index) { @@ -440,6 +550,90 @@ static CEL_NONNULL(cel_Expr*) _cel_AstFromProtoV1Alpha1_CallExpr( _cel_AstFromProtoV1Alpha1_Expr(state, in_args[1])); return cel_Expr_UpCast(expr); } + if (_cel_AstFromProtoV1Alpha1_IsAttributeOrHasField(in)) { + _cel_AstFromProtoV1Alpha1_CheckId(state, id); + cel_AttrExpr* expr = cel_AttrExpr_New(state->ast); + if (CEL_UNLIKELY(expr == cel_nullptr)) { + cel_OutOfMemoryStatus(state->status); + _cel_longjmp(state->jmp); + } + cel_Expr_SetId(cel_Expr_UpCast(expr), id); + _cel_AstFromProtoV1Alpha1_UpdatePosition(state, cel_Expr_UpCast(expr)); + _cel_AstFromProtoV1Alpha1_SetType(state, cel_Expr_UpCast(expr)); + _cel_AstFromProtoV1Alpha1_SetRef(state, cel_Expr_UpCast(expr)); + cel_AttrExpr_SetOperand(expr, + _cel_AstFromProtoV1Alpha1_Expr(state, in_args[0])); + size_t in_quals_len = 0; + const google_api_expr_v1alpha1_Expr* const* in_quals = + google_api_expr_v1alpha1_Expr_CreateList_elements( + google_api_expr_v1alpha1_Expr_list_expr(in_args[1]), &in_quals_len); + for (size_t i = 0; i < in_quals_len; ++i) { + size_t in_qual_len = 0; + const google_api_expr_v1alpha1_Expr* const* in_qual = + google_api_expr_v1alpha1_Expr_CreateList_elements( + google_api_expr_v1alpha1_Expr_list_expr(in_quals[i]), + &in_qual_len); + CEL_ASSERT_GE(in_qual_len, size_t{1}); + cel_AttrQualExpr* qual_expr = cel_AttrQualExpr_New(state->ast); + if (CEL_UNLIKELY(qual_expr == cel_nullptr)) { + cel_OutOfMemoryStatus(state->status); + _cel_longjmp(state->jmp); + } + int64_t qual_expr_id; + if (in_qual_len == 1) { + qual_expr_id = google_api_expr_v1alpha1_Expr_id(in_qual[0]); + switch (google_api_expr_v1alpha1_Constant_constant_kind_case( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[0]))) { + case google_api_expr_v1alpha1_Constant_constant_kind_bool_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_BoolAttributeQualifier( + google_api_expr_v1alpha1_Constant_bool_value( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[0]))); + break; + case google_api_expr_v1alpha1_Constant_constant_kind_int64_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_IntAttributeQualifier( + google_api_expr_v1alpha1_Constant_int64_value( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[0]))); + break; + case google_api_expr_v1alpha1_Constant_constant_kind_uint64_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_UintAttributeQualifier( + google_api_expr_v1alpha1_Constant_uint64_value( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[0]))); + break; + case google_api_expr_v1alpha1_Constant_constant_kind_string_value: + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_StringAttributeQualifier(_cel_AstFromProtoV1Alpha1_StrDup( + state, + google_api_expr_v1alpha1_Constant_string_value( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[0])))); + break; + default: + CEL_UNREACHABLE(); + } + } else { + qual_expr_id = google_api_expr_v1alpha1_Expr_id(in_qual[1]); + *cel_AttrQualExpr_MutableValue(qual_expr) = + cel_StringAttributeQualifier(_cel_AstFromProtoV1Alpha1_StrDup( + state, + google_api_expr_v1alpha1_Constant_string_value( + google_api_expr_v1alpha1_Expr_const_expr(in_qual[1])))); + } + _cel_AstFromProtoV1Alpha1_CheckId(state, qual_expr_id); + cel_Expr_SetId(cel_Expr_UpCast(qual_expr), qual_expr_id); + _cel_AstFromProtoV1Alpha1_UpdatePosition(state, + cel_Expr_UpCast(qual_expr)); + _cel_AstFromProtoV1Alpha1_SetType(state, cel_Expr_UpCast(qual_expr)); + _cel_AstFromProtoV1Alpha1_SetRef(state, cel_Expr_UpCast(qual_expr)); + cel_AttrExpr_AppendQualifier(expr, qual_expr); + } + cel_AttrExpr_SetTestOnly( + expr, + cel_StringView_Equals(google_api_expr_v1alpha1_Expr_Call_function(in), + cel_StringView_From("cel.@hasField"))); + return cel_Expr_UpCast(expr); + } cel_StringView function = google_api_expr_v1alpha1_Expr_Call_function(in); if (!cel_StringView_Empty(function) && !google_api_expr_v1alpha1_Expr_Call_has_target(in)) { diff --git a/cel-c/ast_proto_v1alpha1_test.cc b/cel-c/ast_proto_v1alpha1_test.cc index d2f5ad2..ea4b5d4 100644 --- a/cel-c/ast_proto_v1alpha1_test.cc +++ b/cel-c/ast_proto_v1alpha1_test.cc @@ -27,6 +27,7 @@ #include "cel-c/alloc.h" #include "cel-c/arena.h" #include "cel-c/ast.h" +#include "cel-c/attribute.h" #include "cel-c/config.h" #include "cel-c/constant.h" #include "cel-c/duration.h" @@ -1199,4 +1200,298 @@ TEST_F(AstFromProtoTest, Block) { cel_StringView_From("baz"))); } +TEST_F(AstFromProtoTest, Attribute) { + AstPtr ast(cel_Ast_FromProtoV1Alpha1( + ParseTextProto(R"pb( + source_info: { + positions { key: 1 value: 1 } + positions { key: 2 value: 2 } + positions { key: 3 value: 3 } + positions { key: 4 value: 4 } + positions { key: 5 value: 5 } + positions { key: 6 value: 6 } + positions { key: 7 value: 7 } + positions { key: 8 value: 8 } + positions { key: 9 value: 9 } + positions { key: 10 value: 10 } + positions { key: 11 value: 11 } + positions { key: 12 value: 12 } + positions { key: 13 value: 13 } + positions { key: 14 value: 14 } + } + expr: { + id: 1 + call_expr: { + function: "cel.@attribute" + args { + id: 2 + ident_expr: { name: "baz" } + } + args { + id: 3 + list_expr: { + elements { + id: 4 + list_expr: { + elements { + id: 5 + const_expr: { int64_value: 1 } + } + elements { + id: 6 + const_expr: { string_value: "foo" } + } + } + } + elements { + id: 7 + list_expr: { + elements { + id: 8 + const_expr: { bool_value: true } + } + } + } + elements { + id: 9 + list_expr: { + elements { + id: 10 + const_expr: { int64_value: 2 } + } + } + } + elements { + id: 11 + list_expr: { + elements { + id: 12 + const_expr: { uint64_value: 3 } + } + } + } + elements { + id: 13 + list_expr: { + elements { + id: 14 + const_expr: { string_value: "bar" } + } + } + } + } + } + args { + id: 15 + const_expr: { int64_value: 0 } + } + } + } + )pb"), + arena(), status())); + ASSERT_THAT(ast, NotNull()); + ASSERT_TRUE(cel_Status_Ok(status())); + EXPECT_EQ(cel_Ast_MaxId(ast.get()), 14); + + cel_Expr* expr = cel_Ast_Expr(ast.get()); + ASSERT_THAT(expr, NotNull()); + ASSERT_EQ(cel_Expr_Kind(expr), cel_ExprKind_kAttr); + EXPECT_EQ(cel_Expr_SourcePosition(expr), 1); + + cel_AttrExpr* attr_expr = cel_AttrExpr_DownCast(expr); + ASSERT_THAT(attr_expr, NotNull()); + EXPECT_FALSE(cel_AttrExpr_TestOnly(attr_expr)); + + cel_Expr* operand = cel_AttrExpr_Operand(attr_expr); + ASSERT_THAT(operand, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(operand), 2); + ASSERT_EQ(cel_Expr_Kind(operand), cel_ExprKind_kIdent); + + ASSERT_EQ(cel_AttrExpr_Qualifiers(attr_expr, nullptr, nullptr), 5); + + cel_AttrQualExpr* qual0 = cel_AttrExpr_Qualifier(attr_expr, 0); + ASSERT_THAT(qual0, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual0)), 6); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual0)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual0)), + cel_StringView_From("foo"))); + + cel_AttrQualExpr* qual1 = cel_AttrExpr_Qualifier(attr_expr, 1); + ASSERT_THAT(qual1, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual1)), 8); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual1)), + cel_AttributeQualifierKind_kBool); + EXPECT_TRUE(cel_AttributeQualifier_GetBool(cel_AttrQualExpr_Value(qual1))); + + cel_AttrQualExpr* qual2 = cel_AttrExpr_Qualifier(attr_expr, 2); + ASSERT_THAT(qual2, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual2)), 10); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual2)), + cel_AttributeQualifierKind_kInt); + EXPECT_EQ(cel_AttributeQualifier_GetInt(cel_AttrQualExpr_Value(qual2)), 2); + + cel_AttrQualExpr* qual3 = cel_AttrExpr_Qualifier(attr_expr, 3); + ASSERT_THAT(qual3, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual3)), 12); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual3)), + cel_AttributeQualifierKind_kUint); + EXPECT_EQ(cel_AttributeQualifier_GetUint(cel_AttrQualExpr_Value(qual3)), 3); + + cel_AttrQualExpr* qual4 = cel_AttrExpr_Qualifier(attr_expr, 4); + ASSERT_THAT(qual4, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual4)), 14); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual4)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual4)), + cel_StringView_From("bar"))); +} + +TEST_F(AstFromProtoTest, HasField) { + AstPtr ast(cel_Ast_FromProtoV1Alpha1( + ParseTextProto(R"pb( + source_info: { + positions { key: 1 value: 1 } + positions { key: 2 value: 2 } + positions { key: 3 value: 3 } + positions { key: 4 value: 4 } + positions { key: 5 value: 5 } + positions { key: 6 value: 6 } + positions { key: 7 value: 7 } + positions { key: 8 value: 8 } + positions { key: 9 value: 9 } + positions { key: 10 value: 10 } + positions { key: 11 value: 11 } + positions { key: 12 value: 12 } + positions { key: 13 value: 13 } + positions { key: 14 value: 14 } + } + expr: { + id: 1 + call_expr: { + function: "cel.@hasField" + args { + id: 2 + ident_expr: { name: "baz" } + } + args { + id: 3 + list_expr: { + elements { + id: 4 + list_expr: { + elements { + id: 5 + const_expr: { int64_value: 1 } + } + elements { + id: 6 + const_expr: { string_value: "foo" } + } + } + } + elements { + id: 7 + list_expr: { + elements { + id: 8 + const_expr: { bool_value: true } + } + } + } + elements { + id: 9 + list_expr: { + elements { + id: 10 + const_expr: { int64_value: 2 } + } + } + } + elements { + id: 11 + list_expr: { + elements { + id: 12 + const_expr: { uint64_value: 3 } + } + } + } + elements { + id: 13 + list_expr: { + elements { + id: 14 + const_expr: { string_value: "bar" } + } + } + } + } + } + } + } + )pb"), + arena(), status())); + ASSERT_THAT(ast, NotNull()); + ASSERT_TRUE(cel_Status_Ok(status())); + EXPECT_EQ(cel_Ast_MaxId(ast.get()), 14); + + cel_Expr* expr = cel_Ast_Expr(ast.get()); + ASSERT_THAT(expr, NotNull()); + ASSERT_EQ(cel_Expr_Kind(expr), cel_ExprKind_kAttr); + EXPECT_EQ(cel_Expr_SourcePosition(expr), 1); + + cel_AttrExpr* attr_expr = cel_AttrExpr_DownCast(expr); + ASSERT_THAT(attr_expr, NotNull()); + EXPECT_TRUE(cel_AttrExpr_TestOnly(attr_expr)); + + cel_Expr* operand = cel_AttrExpr_Operand(attr_expr); + ASSERT_THAT(operand, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(operand), 2); + ASSERT_EQ(cel_Expr_Kind(operand), cel_ExprKind_kIdent); + + ASSERT_EQ(cel_AttrExpr_Qualifiers(attr_expr, nullptr, nullptr), 5); + + cel_AttrQualExpr* qual0 = cel_AttrExpr_Qualifier(attr_expr, 0); + ASSERT_THAT(qual0, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual0)), 6); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual0)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual0)), + cel_StringView_From("foo"))); + + cel_AttrQualExpr* qual1 = cel_AttrExpr_Qualifier(attr_expr, 1); + ASSERT_THAT(qual1, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual1)), 8); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual1)), + cel_AttributeQualifierKind_kBool); + EXPECT_TRUE(cel_AttributeQualifier_GetBool(cel_AttrQualExpr_Value(qual1))); + + cel_AttrQualExpr* qual2 = cel_AttrExpr_Qualifier(attr_expr, 2); + ASSERT_THAT(qual2, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual2)), 10); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual2)), + cel_AttributeQualifierKind_kInt); + EXPECT_EQ(cel_AttributeQualifier_GetInt(cel_AttrQualExpr_Value(qual2)), 2); + + cel_AttrQualExpr* qual3 = cel_AttrExpr_Qualifier(attr_expr, 3); + ASSERT_THAT(qual3, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual3)), 12); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual3)), + cel_AttributeQualifierKind_kUint); + EXPECT_EQ(cel_AttributeQualifier_GetUint(cel_AttrQualExpr_Value(qual3)), 3); + + cel_AttrQualExpr* qual4 = cel_AttrExpr_Qualifier(attr_expr, 4); + ASSERT_THAT(qual4, NotNull()); + EXPECT_EQ(cel_Expr_SourcePosition(cel_Expr_UpCast(qual4)), 14); + ASSERT_EQ(cel_AttributeQualifier_Kind(cel_AttrQualExpr_Value(qual4)), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(cel_AttrQualExpr_Value(qual4)), + cel_StringView_From("bar"))); +} + } // namespace diff --git a/cel-c/ast_traverse.cc b/cel-c/ast_traverse.cc index 1416910..383584e 100644 --- a/cel-c/ast_traverse.cc +++ b/cel-c/ast_traverse.cc @@ -73,6 +73,7 @@ typedef enum CEL_ATTRIBUTE_CLOSED_ENUM { _cel_AstTraverserRecordKind_kBindBound, _cel_AstTraverserRecordKind_kBlockBindingBound, _cel_AstTraverserRecordKind_kBlockBound, + _cel_AstTraverserRecordKind_kAttrOperand, } _cel_AstTraverserRecordKind; typedef struct { @@ -135,6 +136,9 @@ struct cel_AstTraverser { // Is the visitor interested in any {Pre,Post}VisitBlockBindingExpr{...} // callbacks? bool block_bound_callbacks; + // Is the visitor interested in any {Pre,Post}VisitAttrExpr{...} + // callbacks? + bool attr_operand_callbacks; }; CEL_ATTRIBUTE_NODISCARD @@ -305,6 +309,14 @@ static _cel_AstVisitorControl _cel_AstTraverserRecord_PreVisit( control = _cel_AstVisitor_Invoke(visitor, PreVisitBlockExpr, cel_BlockExpr_DownCast(expr)); break; + case cel_ExprKind_kAttrQual: + control = _cel_AstVisitor_Invoke(visitor, VisitAttrQualExpr, + cel_AttrQualExpr_DownCast(expr)); + break; + case cel_ExprKind_kAttr: + control = _cel_AstVisitor_Invoke(visitor, PreVisitAttrExpr, + cel_AttrExpr_DownCast(expr)); + break; default: cel_InternalStatus(status, cel_StringView_From("unexpected AST node kind")); @@ -436,6 +448,10 @@ static _cel_AstVisitorControl _cel_AstTraverserRecord_PreVisit( control = _cel_AstVisitor_Invoke(visitor, PreVisitBlockExprBound, expr); break; } + case _cel_AstTraverserRecordKind_kAttrOperand: { + control = _cel_AstVisitor_Invoke(visitor, PreVisitAttrExprOperand, expr); + break; + } default: CEL_UNREACHABLE(); } @@ -553,6 +569,12 @@ static _cel_AstVisitorControl _cel_AstTraverserRecord_PostVisit( control = _cel_AstVisitor_Invoke(visitor, PostVisitBlockExpr, cel_BlockExpr_DownCast(expr)); break; + case cel_ExprKind_kAttrQual: + break; + case cel_ExprKind_kAttr: + control = _cel_AstVisitor_Invoke(visitor, PostVisitAttrExpr, + cel_AttrExpr_DownCast(expr)); + break; default: record->data.postvisited = false; cel_InternalStatus(status, @@ -682,6 +704,10 @@ static _cel_AstVisitorControl _cel_AstTraverserRecord_PostVisit( control = _cel_AstVisitor_Invoke(visitor, PostVisitBlockExprBound, expr); break; } + case _cel_AstTraverserRecordKind_kAttrOperand: { + control = _cel_AstVisitor_Invoke(visitor, PostVisitAttrExprOperand, expr); + break; + } default: CEL_UNREACHABLE(); } @@ -1059,6 +1085,23 @@ static bool _cel_AstTraverser_PushBlockBound(CEL_NONNULL(cel_AstTraverser*) return true; } +CEL_ATTRIBUTE_NODISCARD +static bool _cel_AstTraverser_PushAttrOperand(CEL_NONNULL(cel_AstTraverser*) + traverser, + CEL_NONNULL(const cel_Expr*) + result, + CEL_NONNULL(cel_Status*) status) { + _cel_AstTraverserRecord* record = _cel_AstTraverser_Push(traverser, status); + if (CEL_UNLIKELY(record == cel_nullptr)) { + return false; + } + record->data.expr = result; + record->kind = traverser->attr_operand_callbacks + ? _cel_AstTraverserRecordKind_kAttrOperand + : _cel_AstTraverserRecordKind_kExpr; + return true; +} + CEL_ATTRIBUTE_NODISCARD static bool _cel_AstTraverser_PushSelectDeps(CEL_NONNULL(cel_AstTraverser*) traverser, @@ -1372,6 +1415,30 @@ static bool _cel_AstTraverser_PushBlockDeps(CEL_NONNULL(cel_AstTraverser*) return true; } +CEL_ATTRIBUTE_NODISCARD +static bool _cel_AstTraverser_PushAttrDeps(CEL_NONNULL(cel_AstTraverser*) + traverser, + const cel_AttrExpr* attr_expr, + CEL_NONNULL(cel_Status*) status) { + CEL_NULLABLE(cel_AttrQualExpr*) quals; + size_t quals_size = + cel_AttrExpr_Qualifiers(attr_expr, /*head=*/cel_nullptr, &quals); + for (size_t i = quals_size; i > 0; --i) { + if (!_cel_AstTraverser_PushExpr(traverser, cel_Expr_UpCast(quals), + status)) { + return false; + } + quals = cel_AttrExpr_PrevQualifier(quals); + } + CEL_NULLABLE(cel_Expr*) operand = cel_AttrExpr_Operand(attr_expr); + if (operand != cel_nullptr) { + if (!_cel_AstTraverser_PushAttrOperand(traverser, operand, status)) { + return false; + } + } + return true; +} + CEL_ATTRIBUTE_NODISCARD static bool _cel_AstTraverser_PushDeps(CEL_NONNULL(cel_AstTraverser*) traverser, CEL_NONNULL(_cel_AstTraverserRecord*) @@ -1437,6 +1504,11 @@ static bool _cel_AstTraverser_PushDeps(CEL_NONNULL(cel_AstTraverser*) traverser, case cel_ExprKind_kBlock: return _cel_AstTraverser_PushBlockDeps( traverser, cel_BlockExpr_DownCast(expr), status); + case cel_ExprKind_kAttrQual: + break; + case cel_ExprKind_kAttr: + return _cel_AstTraverser_PushAttrDeps( + traverser, cel_AttrExpr_DownCast(expr), status); default: cel_InternalStatus(status, cel_StringView_From("unexpected AST node kind")); @@ -1487,6 +1559,8 @@ static bool _cel_AstTraverser_PushDeps(CEL_NONNULL(cel_AstTraverser*) traverser, case _cel_AstTraverserRecordKind_kBlockBindingBound: CEL_ATTRIBUTE_FALLTHROUGH; case _cel_AstTraverserRecordKind_kBlockBound: + CEL_ATTRIBUTE_FALLTHROUGH; + case _cel_AstTraverserRecordKind_kAttrOperand: return _cel_AstTraverser_PushExpr(traverser, expr, status); default: CEL_UNREACHABLE(); @@ -1545,6 +1619,8 @@ static void _cel_AstTraverser_Construct(CEL_NONNULL(cel_AstTraverser*) _cel_AstVisitor_HasVisit(visitor, BlockBindingExprBound); traverser->block_bound_callbacks = _cel_AstVisitor_HasVisit(visitor, BlockExprBound); + traverser->attr_operand_callbacks = + _cel_AstVisitor_HasVisit(visitor, AttrExprOperand); } static void _cel_AstTraverser_Destruct(CEL_NONNULL(cel_AstTraverser*) diff --git a/cel-c/ast_visitor.h b/cel-c/ast_visitor.h index 09d2e90..d8dc2b8 100644 --- a/cel-c/ast_visitor.h +++ b/cel-c/ast_visitor.h @@ -232,6 +232,19 @@ typedef void cel_AstVisitorVTable_PreVisitBlockExprBound( typedef void cel_AstVisitorVTable_PostVisitBlockExprBound( CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_Expr*)); +typedef void cel_AstVisitorVTable_VisitAttrQualExpr( + CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_AttrQualExpr*)); + +typedef void cel_AstVisitorVTable_PreVisitAttrExpr( + CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_AttrExpr*)); +typedef void cel_AstVisitorVTable_PostVisitAttrExpr( + CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_AttrExpr*)); + +typedef void cel_AstVisitorVTable_PreVisitAttrExprOperand( + CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_Expr*)); +typedef void cel_AstVisitorVTable_PostVisitAttrExprOperand( + CEL_NONNULL(cel_AstVisitor*), CEL_NONNULL(const cel_Expr*)); + struct cel_AstVisitorVTable { // NOLINTBEGIN(google3-readability-class-member-naming) CEL_NULLABLE(cel_AstVisitorVTable_PreVisitExpr*) PreVisitExpr; @@ -410,6 +423,16 @@ struct cel_AstVisitorVTable { PreVisitBlockExprBound; cel_AstVisitorVTable_PostVisitBlockExprBound* cel_nullable PostVisitBlockExprBound; + + cel_AstVisitorVTable_VisitAttrQualExpr* cel_nullable VisitAttrQualExpr; + + cel_AstVisitorVTable_PreVisitAttrExpr* cel_nullable PreVisitAttrExpr; + cel_AstVisitorVTable_PostVisitAttrExpr* cel_nullable PostVisitAttrExpr; + + cel_AstVisitorVTable_PreVisitAttrExprOperand* cel_nullable + PreVisitAttrExprOperand; + cel_AstVisitorVTable_PostVisitAttrExprOperand* cel_nullable + PostVisitAttrExprOperand; // NOLINTEND(google3-readability-class-member-naming) }; diff --git a/cel-c/attribute.cc b/cel-c/attribute.cc new file mode 100644 index 0000000..48a8331 --- /dev/null +++ b/cel-c/attribute.cc @@ -0,0 +1,56 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cel-c/attribute.h" + +#include + +#include "cel-c/assert.h" +#include "cel-c/config.h" +#include "cel-c/string_view.h" + +extern "C" bool cel_AttributeQualifier_Equals( + CEL_NONNULL(const cel_AttributeQualifier*) lhs, + CEL_NONNULL(const cel_AttributeQualifier*) rhs) { + CEL_ASSERT_NOT_NULL(lhs); + CEL_ASSERT_NOT_NULL(rhs); + + if (lhs == rhs) { + return true; + } + + const cel_AttributeQualifierKind kind = cel_AttributeQualifier_Kind(lhs); + if (kind != cel_AttributeQualifier_Kind(rhs)) { + return false; + } + + switch (kind) { + case cel_AttributeQualifierKind_kUnspecified: + return true; + case cel_AttributeQualifierKind_kBool: + return cel_AttributeQualifier_GetBool(lhs) == + cel_AttributeQualifier_GetBool(rhs); + case cel_AttributeQualifierKind_kInt: + return cel_AttributeQualifier_GetInt(lhs) == + cel_AttributeQualifier_GetInt(rhs); + case cel_AttributeQualifierKind_kUint: + return cel_AttributeQualifier_GetUint(lhs) == + cel_AttributeQualifier_GetUint(rhs); + case cel_AttributeQualifierKind_kString: + return cel_StringView_Equals(cel_AttributeQualifier_GetString(lhs), + cel_AttributeQualifier_GetString(rhs)); + default: + return false; + } +} diff --git a/cel-c/attribute.h b/cel-c/attribute.h new file mode 100644 index 0000000..4dd6b36 --- /dev/null +++ b/cel-c/attribute.h @@ -0,0 +1,225 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_CEL_C_ATTRIBUTE_H_ +#define THIRD_PARTY_CEL_C_ATTRIBUTE_H_ + +#include +#include +#include + +#include "cel-c/assert.h" +#include "cel-c/config.h" +#include "cel-c/string_view.h" + +CEL_BEGIN_DECLS + +typedef enum { + cel_AttributeQualifierKind_kUnspecified = 0, + cel_AttributeQualifierKind_kBool, + cel_AttributeQualifierKind_kInt, + cel_AttributeQualifierKind_kUint, + cel_AttributeQualifierKind_kString, +} cel_AttributeQualifierKind; + +// cel_AttributeQualifier +// +// Represents an attribute qualifier. +typedef struct { +#ifdef _MSC_VER +#pragma pack(push, 4) +#endif + // `alignas(8)` forces `cel_AttributeQualifier` to be 8-byte aligned, + // regardless of `CEL_ATTRIBUTE_PACKED(4)` and platforms where `int64_t` is + // only 4-byte aligned (e.g. 32-bit x86). + alignas(8) union CEL_ATTRIBUTE_PACKED(4) { + bool b; // bool + int64_t i; // int + uint64_t u; // uint + double f; // double + struct CEL_ATTRIBUTE_PACKED(4) { + const char* cel_nullability_unknown data; + uint32_t size; + } s; // string + } data; + cel_AttributeQualifierKind kind; +#ifdef _MSC_VER +#pragma pack(pop) +#endif +} cel_AttributeQualifier; + +CEL_STATIC_ASSERT(sizeof(cel_AttributeQualifier) == 16); +CEL_STATIC_ASSERT(alignof(cel_AttributeQualifier) == 8); + +// cel_UnspecifiedAttributeQualifier +// +// Returns an unspecified attribute qualifier. +#define cel_UnspecifiedAttributeQualifier() \ + ((cel_AttributeQualifier){.kind = cel_AttributeQualifierKind_kUnspecified}) + +// cel_BoolAttributeQualifier +// +// Returns a bool attribute qualifier. +#define cel_BoolAttributeQualifier(val) \ + ((cel_AttributeQualifier){.data = \ + { \ + .b = (val), \ + }, \ + .kind = cel_AttributeQualifierKind_kBool}) + +// cel_IntAttributeQualifier +// +// Returns an int attribute qualifier. +#define cel_IntAttributeQualifier(val) \ + ((cel_AttributeQualifier){.data = \ + { \ + .i = (val), \ + }, \ + .kind = cel_AttributeQualifierKind_kInt}) + +// cel_IntAttributeQualifier +// +// Returns a uint attribute qualifier. +#define cel_UintAttributeQualifier(val) \ + ((cel_AttributeQualifier){.data = \ + { \ + .u = (val), \ + }, \ + .kind = cel_AttributeQualifierKind_kUint}) + +// cel_StringAttributeQualifier +// +// Returns a string attribute qualifier. +#define cel_StringAttributeQualifier(val) \ + ((cel_AttributeQualifier){ \ + .data = \ + { \ + .s = {cel_StringView_Data((val)), cel_StringView_Size32((val))}, \ + }, \ + .kind = cel_AttributeQualifierKind_kString}) + +// cel_AttributeQualifier_SetBool +// +// Sets the attribute qualifier to be bool. +static CEL_INLINE void cel_AttributeQualifier_SetBool( + CEL_NONNULL(cel_AttributeQualifier*) attribute_qualifier, bool val) { + CEL_ASSERT_NOT_NULL(attribute_qualifier); + + *attribute_qualifier = cel_BoolAttributeQualifier(val); +} + +// cel_AttributeQualifier_SetInt +// +// Sets the attribute qualifier to be int. +static CEL_INLINE void cel_AttributeQualifier_SetInt( + CEL_NONNULL(cel_AttributeQualifier*) attribute_qualifier, int64_t val) { + CEL_ASSERT_NOT_NULL(attribute_qualifier); + + *attribute_qualifier = cel_IntAttributeQualifier(val); +} + +// cel_AttributeQualifier_SetUint +// +// Sets the attribute qualifier to be uint. +static CEL_INLINE void cel_AttributeQualifier_SetUint( + CEL_NONNULL(cel_AttributeQualifier*) attribute_qualifier, uint64_t val) { + CEL_ASSERT_NOT_NULL(attribute_qualifier); + + *attribute_qualifier = cel_UintAttributeQualifier(val); +} + +// cel_AttributeQualifier_SetString +// +// Sets the attribute qualifier to be string. +static CEL_INLINE void cel_AttributeQualifier_SetString( + CEL_NONNULL(cel_AttributeQualifier*) attribute_qualifier, + cel_StringView val) { + CEL_ASSERT_NOT_NULL(attribute_qualifier); + + *attribute_qualifier = cel_StringAttributeQualifier(val); +} + +// cel_AttributeQualifier_Kind +// +// Returns the kind of the attribute qualifier. +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE cel_AttributeQualifierKind cel_AttributeQualifier_Kind( + CEL_NONNULL(const cel_AttributeQualifier*) attribute_qualifier) { + CEL_ASSERT_NOT_NULL(attribute_qualifier); + + return attribute_qualifier->kind; +} + +// cel_AttributeQualifier_GetBool +// +// Asserts that the attribute qualifier is a bool and returns the value. +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE bool cel_AttributeQualifier_GetBool( + CEL_NONNULL(const cel_AttributeQualifier*) attribute_qualifier) { + CEL_ASSERT_EQ(cel_AttributeQualifier_Kind(attribute_qualifier), + cel_AttributeQualifierKind_kBool); + + return attribute_qualifier->data.b; +} + +// cel_AttributeQualifier_GetInt +// +// Asserts that the attribute qualifier is an int and returns the value. +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE int64_t cel_AttributeQualifier_GetInt( + CEL_NONNULL(const cel_AttributeQualifier*) attribute_qualifier) { + CEL_ASSERT_EQ(cel_AttributeQualifier_Kind(attribute_qualifier), + cel_AttributeQualifierKind_kInt); + + return attribute_qualifier->data.i; +} + +// cel_AttributeQualifier_GetUint +// +// Asserts that the attribute qualifier is a uint and returns the value. +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE uint64_t cel_AttributeQualifier_GetUint( + CEL_NONNULL(const cel_AttributeQualifier*) attribute_qualifier) { + CEL_ASSERT_EQ(cel_AttributeQualifier_Kind(attribute_qualifier), + cel_AttributeQualifierKind_kUint); + + return attribute_qualifier->data.u; +} + +// cel_AttributeQualifier_GetString +// +// Asserts that the attribute qualifier is a string and returns the value. +CEL_ATTRIBUTE_NODISCARD +static CEL_INLINE cel_StringView cel_AttributeQualifier_GetString( + CEL_NONNULL(const cel_AttributeQualifier*) attribute_qualifier) { + CEL_ASSERT_EQ(cel_AttributeQualifier_Kind(attribute_qualifier), + cel_AttributeQualifierKind_kString); + + return cel_StringView_FromArray(attribute_qualifier->data.s.data, + attribute_qualifier->data.s.size); +} + +// cel_AttributeQualifier_Equals +// +// Tests two attribute qualifiers for equality. To be considered equal, the +// attribute qualifiers must have the same kind and value. +CEL_ATTRIBUTE_NODISCARD +CEL_ATTRIBUTE_NOTHROW +CEL_EXTERN bool cel_AttributeQualifier_Equals( + CEL_NONNULL(const cel_AttributeQualifier*) lhs, + CEL_NONNULL(const cel_AttributeQualifier*) rhs); + +CEL_END_DECLS + +#endif // THIRD_PARTY_CEL_C_ATTRIBUTE_H_ diff --git a/cel-c/attribute_test.cc b/cel-c/attribute_test.cc new file mode 100644 index 0000000..07a38ce --- /dev/null +++ b/cel-c/attribute_test.cc @@ -0,0 +1,115 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cel-c/attribute.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "cel-c/config.h" +#include "cel-c/string_view.h" + +namespace { + +using ::testing::_; + +TEST(AttributeQualifier, Unspecified) { + cel_AttributeQualifier attribute_qualifier = + cel_UnspecifiedAttributeQualifier(); + EXPECT_EQ(cel_AttributeQualifier_Kind(&attribute_qualifier), + cel_AttributeQualifierKind_kUnspecified); + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetBool(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetInt(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetUint(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetString(&attribute_qualifier)), _); +#endif +} + +TEST(AttributeQualifier, Bool) { + cel_AttributeQualifier attribute_qualifier; + cel_AttributeQualifier_SetBool(&attribute_qualifier, true); + EXPECT_EQ(cel_AttributeQualifier_Kind(&attribute_qualifier), + cel_AttributeQualifierKind_kBool); + EXPECT_TRUE(cel_AttributeQualifier_GetBool(&attribute_qualifier)); + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetInt(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetUint(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetString(&attribute_qualifier)), _); +#endif +} + +TEST(AttributeQualifier, Int) { + cel_AttributeQualifier attribute_qualifier; + cel_AttributeQualifier_SetInt(&attribute_qualifier, 1); + EXPECT_EQ(cel_AttributeQualifier_Kind(&attribute_qualifier), + cel_AttributeQualifierKind_kInt); + EXPECT_EQ(cel_AttributeQualifier_GetInt(&attribute_qualifier), 1); + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetBool(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetUint(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetString(&attribute_qualifier)), _); +#endif +} + +TEST(AttributeQualifier, Uint) { + cel_AttributeQualifier attribute_qualifier; + cel_AttributeQualifier_SetUint(&attribute_qualifier, 1); + EXPECT_EQ(cel_AttributeQualifier_Kind(&attribute_qualifier), + cel_AttributeQualifierKind_kUint); + EXPECT_EQ(cel_AttributeQualifier_GetUint(&attribute_qualifier), 1); + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetBool(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetInt(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetString(&attribute_qualifier)), _); +#endif +} + +TEST(AttributeQualifier, String) { + cel_AttributeQualifier attribute_qualifier; + cel_AttributeQualifier_SetString(&attribute_qualifier, + cel_StringView_FromString("foo")); + EXPECT_EQ(cel_AttributeQualifier_Kind(&attribute_qualifier), + cel_AttributeQualifierKind_kString); + EXPECT_TRUE(cel_StringView_Equals( + cel_AttributeQualifier_GetString(&attribute_qualifier), + cel_StringView_FromString("foo"))); + +#ifndef NDEBUG + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetBool(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetInt(&attribute_qualifier)), _); + EXPECT_DEATH_IF_SUPPORTED( + CEL_USED(cel_AttributeQualifier_GetUint(&attribute_qualifier)), _); +#endif +} + +} // namespace