Files
regorus/src/compiler/context.rs
Anand Krishnamoorthi 49958c2ece chore: Make clippy clean and harden helpers (#532)
- Promote common accessors (Expr/Rule span/eidx, ScopeContext constructors, Engine::set_rego_v0) to const
- Prefer Option combinators (map_or, then_some) and map_or_else
- Tighten engine logic: add missing semicolons, use checked u32::try_from, make boolean query evaluation avoid unchecked indexing,

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-12-29 14:47:09 -06:00

195 lines
6.3 KiB
Rust

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![allow(clippy::unused_trait_names)]
//! Compilation context types shared across compiler components.
//!
//! This module defines context structures used for tracking scope-level information
//! during compilation and analysis phases. These types are designed to be compatible
//! with both the interpreter's loop hoisting and the RVM compiler.
use crate::ast::ExprRef;
use alloc::collections::BTreeSet;
use alloc::string::{String, ToString};
/// Type of compilation context for tracking different scenarios
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContextType {
/// Rule context (Complete, PartialSet, PartialObject, or Function)
Rule,
/// Comprehension context (Array, Set, or Object)
Comprehension,
/// Every quantifier context
Every,
/// Query/statement context (no output expressions)
Query,
}
/// Context for tracking variable bindings and output expressions within a scope.
/// Used during loop hoisting and compilation to determine what needs to be hoisted
/// and what's already bound.
///
/// This design is compatible with RVM's CompilationContext for potential future unification.
#[derive(Debug, Clone)]
pub struct ScopeContext {
/// Type of context (Rule, Comprehension, Every, Query)
#[allow(dead_code)]
pub context_type: ContextType,
/// Variables that are bound in the current scope
pub bound_vars: BTreeSet<String>,
/// Variables that are introduced in this scope (used for conflict detection)
pub current_scope_bound_vars: BTreeSet<String>,
/// Variables that are explicitly marked as unbound (from `some` declarations)
pub unbound_vars: BTreeSet<String>,
/// Variables that are local to this scope and will become bound once assigned
pub local_vars: BTreeSet<String>,
/// Flag indicating whether scheduler scope information was available
pub has_scheduler_scope: bool,
/// Key expression from rule head or object comprehension (for output expression hoisting)
#[allow(dead_code)]
pub key_expr: Option<ExprRef>,
/// Value expression from rule assignment or comprehension term (for output expression hoisting)
#[allow(dead_code)]
pub value_expr: Option<ExprRef>,
/// Shared set of module-level globals available in this scope
pub module_globals: Option<crate::Rc<BTreeSet<String>>>,
}
impl ScopeContext {
/// Create a new context with Query type (default, no output expressions)
pub const fn new() -> Self {
Self {
context_type: ContextType::Query,
bound_vars: BTreeSet::new(),
current_scope_bound_vars: BTreeSet::new(),
unbound_vars: BTreeSet::new(),
local_vars: BTreeSet::new(),
has_scheduler_scope: false,
key_expr: None,
value_expr: None,
module_globals: None,
}
}
/// Create a new context with a specific context type
#[allow(dead_code)]
pub const fn with_context_type(context_type: ContextType) -> Self {
Self {
context_type,
bound_vars: BTreeSet::new(),
current_scope_bound_vars: BTreeSet::new(),
unbound_vars: BTreeSet::new(),
local_vars: BTreeSet::new(),
has_scheduler_scope: false,
key_expr: None,
value_expr: None,
module_globals: None,
}
}
/// Create a new context with output expressions (for rules and comprehensions)
#[allow(dead_code)]
pub const fn with_output_exprs(
context_type: ContextType,
key_expr: Option<ExprRef>,
value_expr: Option<ExprRef>,
) -> Self {
Self {
context_type,
bound_vars: BTreeSet::new(),
current_scope_bound_vars: BTreeSet::new(),
unbound_vars: BTreeSet::new(),
local_vars: BTreeSet::new(),
has_scheduler_scope: false,
key_expr,
value_expr,
module_globals: None,
}
}
/// Create a child context that inherits bindings but overrides context type and output expressions
pub fn child_with_output_exprs(
&self,
context_type: ContextType,
key_expr: Option<ExprRef>,
value_expr: Option<ExprRef>,
) -> Self {
Self {
context_type,
bound_vars: self.bound_vars.clone(),
current_scope_bound_vars: BTreeSet::new(),
unbound_vars: self.unbound_vars.clone(),
local_vars: self.local_vars.clone(),
has_scheduler_scope: self.has_scheduler_scope,
key_expr,
value_expr,
module_globals: self.module_globals.clone(),
}
}
/// Add a variable to the bound set
pub fn bind_variable(&mut self, var_name: &str) {
if var_name != "_" {
self.bound_vars.insert(var_name.to_string());
self.current_scope_bound_vars.insert(var_name.to_string());
self.unbound_vars.remove(var_name);
self.local_vars.remove(var_name);
}
}
/// Mark a variable as unbound
pub fn add_unbound_variable(&mut self, var_name: &str) {
if var_name != "_" {
self.bound_vars.remove(var_name);
self.current_scope_bound_vars.remove(var_name);
self.local_vars.remove(var_name);
self.unbound_vars.insert(var_name.to_string());
}
}
/// Check if a variable is known to be unbound
pub fn is_unbound(&self, var_name: &str) -> bool {
self.unbound_vars.contains(var_name)
}
/// Check if we can determine that a variable should be treated as a loop iterator
/// (either it's unbound or explicitly marked as such)
pub fn should_hoist_as_loop(&self, var_name: &str) -> bool {
if var_name == "_" {
return true;
}
if self
.module_globals
.as_ref()
.is_some_and(|globals| globals.contains(var_name))
{
return false;
}
if self.is_unbound(var_name) {
return true;
}
if self.has_scheduler_scope {
return self.local_vars.contains(var_name);
}
!self.bound_vars.contains(var_name)
}
}
impl Default for ScopeContext {
fn default() -> Self {
Self::new()
}
}