mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
- 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>
195 lines
6.3 KiB
Rust
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()
|
|
}
|
|
}
|