#![allow(clippy::unused_trait_names)]
use crate::ast::ExprRef;
use alloc::collections::BTreeSet;
use alloc::string::{String, ToString};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContextType {
Rule,
Comprehension,
Every,
Query,
}
#[derive(Debug, Clone)]
pub struct ScopeContext {
#[allow(dead_code)]
pub context_type: ContextType,
pub bound_vars: BTreeSet<String>,
pub current_scope_bound_vars: BTreeSet<String>,
pub unbound_vars: BTreeSet<String>,
pub local_vars: BTreeSet<String>,
pub has_scheduler_scope: bool,
#[allow(dead_code)]
pub key_expr: Option<ExprRef>,
#[allow(dead_code)]
pub value_expr: Option<ExprRef>,
pub module_globals: Option<crate::Rc<BTreeSet<String>>>,
}
impl ScopeContext {
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,
}
}
#[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,
}
}
#[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,
}
}
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(),
}
}
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);
}
}
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());
}
}
pub fn is_unbound(&self, var_name: &str) -> bool {
self.unbound_vars.contains(var_name)
}
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()
}
}