| 56 | } // namespace |
| 57 | |
| 58 | absl::flat_hash_set<std::string> ExtractFieldPaths( |
| 59 | const cel::expr::Expr& expr) { |
| 60 | NavigableProtoAst ast = NavigableProtoAst::Build(expr); |
| 61 | |
| 62 | absl::flat_hash_set<std::string> field_paths; |
| 63 | std::vector<std::string> fields_in_scope; |
| 64 | |
| 65 | // Preorder traversal works because the select nodes (in a well-formed |
| 66 | // expression) always have only one operand, so its operand is visited |
| 67 | // next in the loop iteration (which results in the path being extended, |
| 68 | // completed, or discarded if uninteresting). |
| 69 | for (const cel::NavigableProtoAstNode& node : |
| 70 | ast.Root().DescendantsPreorder()) { |
| 71 | if (node.node_kind() == cel::NodeKind::kSelect) { |
| 72 | fields_in_scope.push_back(node.expr()->select_expr().field()); |
| 73 | continue; |
| 74 | } |
| 75 | if (node.node_kind() == cel::NodeKind::kIdent && |
| 76 | !IsComprehensionDefinedField(node)) { |
| 77 | fields_in_scope.push_back(node.expr()->ident_expr().name()); |
| 78 | std::reverse(fields_in_scope.begin(), fields_in_scope.end()); |
| 79 | field_paths.insert(absl::StrJoin(fields_in_scope, ".")); |
| 80 | } |
| 81 | fields_in_scope.clear(); |
| 82 | } |
| 83 | |
| 84 | return field_paths; |
| 85 | } |
| 86 | |
| 87 | } // namespace cel |