pub mod ctx;
mod executors;
pub mod graph;
use graph::Graph;
use imbl::HashMap;
use logisheets_base::{BlockFieldId, BlockId, CubeId, ExtRefId, NameId, RangeId, SheetId};
use logisheets_parser::ast;
use crate::CellId;
use self::ctx::FormulaExecCtx;
use self::executors::{add_ast_node, get_all_vertices_from_ast, rebuild_range_deps};
pub use executors::FormulaExecutor;
#[derive(Debug, Clone)]
pub struct FormulaManager {
pub graph: Graph<Vertex>,
pub formulas: HashMap<(SheetId, CellId), ast::Node>,
pub names: HashMap<NameId, ast::Node>,
}
impl FormulaManager {
pub fn new() -> Self {
FormulaManager {
graph: Graph::<Vertex>::new(),
formulas: HashMap::new(),
names: HashMap::new(),
}
}
pub fn add_ast_node(
&mut self,
sheet_id: SheetId,
cell_id: CellId,
range_id: RangeId,
ast: ast::Node,
) {
add_ast_node(self, sheet_id, cell_id, range_id, ast)
}
pub fn rebuild_range_deps<C: FormulaExecCtx>(&mut self, ctx: &C) {
rebuild_range_deps(self, ctx)
}
pub fn set_name<C: FormulaExecCtx>(&mut self, id: NameId, ast: ast::Node, ctx: &C) {
self.unlink_name(id);
let this = Vertex::Name(id);
let mut deps = std::collections::HashSet::<Vertex>::new();
get_all_vertices_from_ast(&ast, &mut deps);
for dep in deps {
self.graph.add_dep(this.clone(), dep.clone());
for range_dep in ctx.get_range_deps(&dep) {
self.graph.add_dep(dep.clone(), range_dep);
}
}
self.names.insert(id, ast);
}
pub fn remove_name(&mut self, id: NameId) -> bool {
self.unlink_name(id);
self.names.remove(&id).is_some()
}
fn unlink_name(&mut self, id: NameId) {
let this = Vertex::Name(id);
if let Some(deps) = self.graph.get_deps(&this).cloned() {
deps.iter().for_each(|d| self.graph.remove_dep(&this, d));
}
}
pub fn name_reaches(&self, ast: &ast::Node, target: NameId) -> bool {
let mut stack = vec![];
collect_names(ast, &mut stack);
let mut seen = std::collections::HashSet::new();
while let Some(n) = stack.pop() {
if n == target {
return true;
}
if !seen.insert(n) {
continue;
}
if let Some(def) = self.names.get(&n) {
collect_names(def, &mut stack);
}
}
false
}
}
fn collect_names(ast: &ast::Node, out: &mut Vec<NameId>) {
match &ast.pure {
ast::PureNode::Func(func) => func.args.iter().for_each(|a| collect_names(a, out)),
ast::PureNode::Reference(ast::CellReference::Name(n)) => out.push(*n),
ast::PureNode::BlockRef(ast::BlockRefNode::Single { key, .. }) => collect_names(key, out),
ast::PureNode::BlockRef(ast::BlockRefNode::Multi {
key_condition,
field_condition,
..
}) => {
collect_names(key_condition, out);
collect_names(field_condition, out);
}
_ => {}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Vertex {
Range(SheetId, RangeId),
Cube(CubeId),
Ext(ExtRefId),
Name(NameId),
Block(SheetId, BlockId, BlockFieldId),
BlockKey(SheetId, BlockId),
BlockAll(SheetId, BlockId),
}
impl Ord for Vertex {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(Vertex::Range(_, r1), Vertex::Range(_, r2)) => r1.cmp(r2),
(Vertex::Cube(c1), Vertex::Cube(c2)) => c1.cmp(c2),
(Vertex::Ext(e1), Vertex::Ext(e2)) => e1.cmp(e2),
(Vertex::Name(n1), Vertex::Name(n2)) => n1.cmp(n2),
(Vertex::Range(_, _), _) => std::cmp::Ordering::Less,
(Vertex::Name(_), _) => std::cmp::Ordering::Less,
(Vertex::Cube(_), _) => std::cmp::Ordering::Greater,
_ => std::cmp::Ordering::Equal,
}
}
}
impl PartialOrd for Vertex {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}