LCOV - code coverage report
Current view: top level - backend/engine/semantic - frame_stack.h (source / functions) Coverage Total Hit
Test: _coverage_report.dat Lines: 100.0 % 3 3
Test Date: 2026-07-02 21:01:18 Functions: 100.0 % 1 1

            Line data    Source code
       1              : #ifndef BIGQUERY_EMULATOR_BACKEND_ENGINE_SEMANTIC_FRAME_STACK_H_
       2              : #define BIGQUERY_EMULATOR_BACKEND_ENGINE_SEMANTIC_FRAME_STACK_H_
       3              : 
       4              : // Name-keyed `Value` frame stack shared by the script driver and
       5              : // the UDF / TVF invocation surface.
       6              : //
       7              : // Two contexts in the semantic executor need a stack of name ->
       8              : // `Value` frames with case-insensitive identifier matching:
       9              : //
      10              : //   * BigQuery scripting (`DECLARE`, `SET`, `BEGIN ... END`,
      11              : //     `IF` / `WHILE` / ...). Each `BEGIN ... END` block pushes a
      12              : //     frame, declarations register fresh bindings in the innermost
      13              : //     frame, and the innermost matching binding wins for `Set` /
      14              : //     `Lookup`. Owned by
      15              : //     `docs/ENGINE_POLICY.md`.
      16              : //   * SQL UDF / TVF invocation. Each call binds the argument list
      17              : //     into a fresh frame the body's `ResolvedArgumentRef` /
      18              : //     `ResolvedRelationArgumentScan` nodes resolve against. Owned by
      19              : //     `docs/ENGINE_POLICY.md`.
      20              : //
      21              : // Both contexts share the same primitive: a stack of frames keyed
      22              : // on the analyzer-lowered identifier, with `Value` payloads. The
      23              : // type lives in `backend/engine/semantic/` (one level above
      24              : // `script/`) so the UDF / TVF call sites can depend on it without
      25              : // taking a transitive dependency on the script-statement
      26              : // translation units.
      27              : //
      28              : // Thread-safety: a `FrameStack` is owned by exactly one in-flight
      29              : // invocation (script run or UDF / TVF call). Callers do not share
      30              : // it across threads. The owning driver constructs one per run and
      31              : // destroys it when the run ends.
      32              : 
      33              : #include <string>
      34              : #include <vector>
      35              : 
      36              : #include "absl/container/flat_hash_map.h"
      37              : #include "absl/status/status.h"
      38              : #include "absl/status/statusor.h"
      39              : #include "absl/strings/string_view.h"
      40              : #include "backend/engine/semantic/value.h"
      41              : 
      42              : namespace bigquery_emulator {
      43              : namespace backend {
      44              : namespace engine {
      45              : namespace semantic {
      46              : 
      47              : // A stack of `(name -> Value)` frames. The constructor opens the
      48              : // outer (global) frame so callers do not have to special-case
      49              : // top-level declarations; `frame_count()` is therefore always >= 1
      50              : // once construction returns.
      51              : //
      52              : // BigQuery identifier comparison is case-insensitive (see the
      53              : // BigQuery scripting reference for `DECLARE` and the UDF /
      54              : // argument resolution rules); names are lower-cased on `Declare` /
      55              : // `Lookup` / `Set` so `DECLARE x` followed by `SET X = 1` (or a
      56              : // UDF body referencing `X` when the analyzer canonicalized the
      57              : // argument name to `x`) resolves to the same binding.
      58              : class FrameStack {
      59              :  public:
      60              :   FrameStack();
      61              :   ~FrameStack();
      62              : 
      63              :   FrameStack(const FrameStack&) = delete;
      64              :   FrameStack& operator=(const FrameStack&) = delete;
      65              : 
      66              :   // Open a new frame. Bindings declared in this frame disappear
      67              :   // when `PopFrame` is called.
      68              :   void PushFrame();
      69              : 
      70              :   // Close the most recent frame. Returns `kFailedPrecondition` if
      71              :   // there is no frame above the outer (global) frame to pop;
      72              :   // popping the outer frame is a programmer error (the call stack
      73              :   // would no longer support `Declare`).
      74              :   absl::Status PopFrame();
      75              : 
      76              :   // Number of frames currently on the stack (always >= 1).
      77            4 :   size_t frame_count() const {
      78            4 :     return frames_.size();
      79            4 :   }
      80              : 
      81              :   // Register a fresh binding in the innermost frame. Returns
      82              :   // `kAlreadyExists` if `name` is already bound in that frame
      83              :   // (BigQuery rejects `DECLARE x` after a previous `DECLARE x` in
      84              :   // the same block, and UDF / TVF signatures may not declare the
      85              :   // same argument name twice).
      86              :   absl::Status Declare(absl::string_view name, Value value);
      87              : 
      88              :   // Update the innermost binding for `name`. Returns `kNotFound`
      89              :   // when no frame on the stack has a binding for `name`.
      90              :   absl::Status Set(absl::string_view name, Value value);
      91              : 
      92              :   // Read the innermost binding for `name`. Returns `kNotFound`
      93              :   // when no frame on the stack has a binding for `name`.
      94              :   absl::StatusOr<Value> Lookup(absl::string_view name) const;
      95              : 
      96              :   // Convenience: does any frame on the stack have a binding for
      97              :   // `name`?
      98              :   bool Has(absl::string_view name) const;
      99              : 
     100              :   // All bindings visible from the current stack position. Inner
     101              :   // frames shadow outer frames (same resolution order as `Lookup`).
     102              :   absl::flat_hash_map<std::string, Value> VisibleBindings() const;
     103              : 
     104              :  private:
     105              :   // Each frame is a flat map keyed on the analyzer-lowered name.
     106              :   // The payload is the current `Value`; updates via `Set`
     107              :   // overwrite the existing entry in the innermost frame that owns
     108              :   // the name (rather than allocating a fresh slot in the top
     109              :   // frame).
     110              :   using Frame = absl::flat_hash_map<std::string, Value>;
     111              :   std::vector<Frame> frames_{};
     112              : };
     113              : 
     114              : }  // namespace semantic
     115              : }  // namespace engine
     116              : }  // namespace backend
     117              : }  // namespace bigquery_emulator
     118              : 
     119              : #endif  // BIGQUERY_EMULATOR_BACKEND_ENGINE_SEMANTIC_FRAME_STACK_H_
        

Generated by: LCOV version 2.0-1