use super::{
docs::{ConstDoc, Docs, QueryDoc},
module::{self, Content, GetMod, Manager},
query::{OperatorDefinition, PipelineDefinition, ScriptDefinition, WindowDefinition},
raw::LocalPathRaw,
warning::{self, Warning, Warnings},
ConnectorDefinition, Const, DeployFlow, FlowDefinition, FnDefn, InvokeAggrFn, NodeId,
};
use crate::{
errors::Result,
pos::Span,
prelude::*,
registry::{Aggr as AggrRegistry, Registry},
NodeMeta,
};
use beef::Cow;
use halfbrown::HashMap;
use std::{collections::BTreeSet, mem};
#[derive(Default, Debug, Clone, Serialize, PartialEq)]
pub struct Scope<'script> {
pub(crate) modules: std::collections::BTreeMap<String, module::Index>,
pub content: Content<'script>,
pub(crate) parent: Option<Box<Scope<'script>>>,
}
impl<'script> Scope<'script> {
pub(crate) fn get_module(&self, id: &[String]) -> Result<Option<module::Index>> {
let (first, rest) = if let Some(r) = id.split_first() {
r
} else {
return Ok(None);
};
let id = if let Some(i) = self.modules.get(first) {
*i
} else {
return Ok(None);
};
Manager::find_module(id, rest)
}
pub(crate) fn add_module_alias(&mut self, alias: String, module_id: module::Index) {
self.modules.insert(alias, module_id);
}
pub(crate) fn insert_flow(&mut self, flow: FlowDefinition<'script>) -> Result<()> {
self.content.insert_flow(flow)
}
pub(crate) fn insert_connector(
&mut self,
connector: ConnectorDefinition<'script>,
) -> Result<()> {
self.content.insert_connector(connector)
}
pub(crate) fn insert_const(&mut self, c: Const<'script>) -> Result<()> {
self.content.insert_const(c)
}
pub(crate) fn insert_function(&mut self, f: FnDefn<'script>) -> Result<()> {
self.content.insert_function(f)
}
pub(crate) fn insert_pipeline(&mut self, pipeline: PipelineDefinition<'script>) -> Result<()> {
self.content.insert_pipeline(pipeline)
}
pub(crate) fn insert_window(&mut self, window: WindowDefinition<'script>) -> Result<()> {
self.content.insert_window(window)
}
pub(crate) fn insert_operator(&mut self, operator: OperatorDefinition<'script>) -> Result<()> {
self.content.insert_operator(operator)
}
pub(crate) fn insert_script(&mut self, script: ScriptDefinition<'script>) -> Result<()> {
self.content.insert_script(script)
}
}
#[allow(clippy::struct_excessive_bools)]
pub struct Helper<'script, 'registry>
where
'script: 'registry,
{
pub(crate) reg: &'registry Registry,
pub(crate) aggr_reg: &'registry AggrRegistry,
pub(crate) can_emit: bool,
pub(crate) is_in_aggr: bool,
pub(crate) instances: HashMap<String, DeployFlow<'script>>,
pub aggregates: Vec<InvokeAggrFn<'script>>,
pub warnings: Warnings,
pub(crate) shadowed_vars: Vec<String>,
pub(crate) locals: HashMap<String, usize>,
pub(crate) docs: Docs,
pub(crate) possible_leaf: bool,
pub(crate) fn_argc: usize,
pub(crate) is_open: bool,
pub scope: Scope<'script>,
}
impl<'script, 'registry> Helper<'script, 'registry>
where
'script: 'registry,
{
pub(crate) fn scope(&mut self) -> &mut Scope<'script> {
&mut self.scope
}
pub(crate) fn enter_scope(&mut self) {
self.set_scope(Scope::default());
}
pub(crate) fn set_scope(&mut self, mut scope: Scope<'script>) {
std::mem::swap(&mut self.scope, &mut scope);
self.scope.parent = Some(Box::new(scope));
}
pub(crate) fn leave_scope(&mut self) -> Result<Scope<'script>> {
if let Some(mut next) = self.scope.parent.take() {
std::mem::swap(&mut self.scope, &mut next);
Ok(*next)
} else {
Err("No parent scope".into())
}
}
pub fn get<Target>(&self, id: &NodeId) -> Result<Option<Target>>
where
Target: 'script,
Manager: module::Get<Target>,
Content<'script>: module::GetMod<Target>,
{
if id.module.is_empty() {
Ok(self.scope.content.get(&id.id))
} else if let Some((m, n)) = self.resolve_module_alias(id)? {
Manager::get(m, n)
} else {
Ok(None)
}
}
pub(crate) fn resolve_module_alias<'n>(
&self,
id: &'n NodeId,
) -> Result<Option<(module::Index, &'n str)>> {
Ok(if id.module.is_empty() {
None
} else {
self.scope.get_module(&id.module)?.map(|mid| (mid, id.id()))
})
}
pub(crate) fn is_const(&self, id: &str) -> bool {
self.scope.content.consts.contains_key(id)
}
pub(crate) fn is_const_path(&self, p: &LocalPathRaw) -> bool {
self.is_const(&p.root.id)
}
pub(crate) fn add_const_doc<N: ToString>(
&mut self,
name: &N,
doc: Option<Vec<Cow<'script, str>>>,
value_type: ValueType,
) {
let doc = doc.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n"));
self.docs.consts.push(ConstDoc {
name: name.to_string(),
doc,
value_type,
});
}
pub(crate) fn add_query_doc<N: ToString>(
&mut self,
name: &N,
doc: Option<Vec<Cow<'script, str>>>,
) {
let doc = doc.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n"));
self.docs.queries.push(QueryDoc {
name: name.to_string(),
doc,
});
}
pub(crate) fn has_locals(&self) -> bool {
self.locals
.iter()
.any(|(n, _)| !n.starts_with(" __SHADOW "))
}
pub(crate) fn swap(
&mut self,
aggregates: &mut Vec<InvokeAggrFn<'script>>,
locals: &mut HashMap<String, usize>,
) {
mem::swap(&mut self.aggregates, aggregates);
mem::swap(&mut self.locals, locals);
}
#[must_use]
pub fn new(reg: &'registry Registry, aggr_reg: &'registry AggrRegistry) -> Self {
Helper {
reg,
aggr_reg,
can_emit: true,
is_in_aggr: false,
instances: HashMap::new(),
aggregates: Vec::new(),
warnings: BTreeSet::new(),
locals: HashMap::new(),
shadowed_vars: Vec::new(),
docs: Docs::default(),
possible_leaf: false,
fn_argc: 0,
is_open: false,
scope: Scope::default(),
}
}
pub(crate) fn register_shadow_var(&mut self, id: &str) -> usize {
let r = self.reserve_shadow();
self.shadowed_vars.push(id.to_string());
r
}
pub(crate) fn register_shadow_from_mid(&mut self, mid: &NodeMeta) -> usize {
let id = format!("{mid:?}");
self.register_shadow_var(&id)
}
pub(crate) fn end_shadow_var(&mut self) {
self.shadowed_vars.pop();
}
fn find_shadow_var(&self, id: &str) -> Option<String> {
let mut r = None;
for (i, s) in self.shadowed_vars.iter().enumerate() {
if s == id {
r = Some(shadow_name(i));
}
}
r
}
fn reserve_shadow(&mut self) -> usize {
self.var_id(&shadow_name(self.shadowed_vars.len()))
}
pub(crate) fn reserve_2_shadow(&mut self) -> (usize, usize) {
let l = self.shadowed_vars.len();
let n1 = shadow_name(l);
let n2 = shadow_name(l + 1);
(self.var_id(&n1), self.var_id(&n2))
}
pub(crate) fn var_id(&mut self, id: &str) -> usize {
let id = self.find_shadow_var(id).unwrap_or_else(|| id.to_string());
self.locals.get(id.as_str()).copied().unwrap_or_else(|| {
self.locals.insert(id.to_string(), self.locals.len());
self.locals.len() - 1
})
}
pub(crate) fn warn<S: ToString>(
&mut self,
outer: Span,
inner: Span,
msg: &S,
class: warning::Class,
) {
self.warnings.insert(Warning::new(outer, inner, msg, class));
}
pub(crate) fn warn_with_scope<S: ToString>(&mut self, r: Span, msg: &S, class: warning::Class) {
self.warnings.insert(Warning::new_with_scope(r, msg, class));
}
}
fn shadow_name(id: usize) -> String {
format!(" __SHADOW {id}__ ")
}