// MoonDatalog —— 分层(Stratification)
//
// 对带否定(`not`)的 Datalog 程序做分层检查与排序:
// 1. 构建谓词依赖图(否定边单独标记);
// 2. 用 Tarjan 算法求强连通分量(SCC);
// 3. 若某个 SCC 内含否定边,则程序不可分层(非 stratifiable);
// 4. 否则按 SCC 的拓扑序(依赖在前)组织规则,逐层求值。
//
// 分层保证:求值某层时,其否定原子引用的关系已完全确定,
// 从而 `not` 的语义(在已推导集合上的补集)是良定义的。
///|
/// Tarjan 强连通分量算法的工作状态。
priv struct Tarjan {
/// 邻接表:谓词 -> 其后继(规则头部依赖的主体谓词)
adj : @hashmap.HashMap[String, Array[String]]
/// 否定依赖边集合
neg : @hashset.HashSet[(String, String)]
mut idx_counter : Int
index : @hashmap.HashMap[String, Int]
lowlink : @hashmap.HashMap[String, Int]
on_stack : @hashset.HashSet[String]
stack : Array[String]
/// 完成顺序收集的 SCC(先完成的在列表前部,即反向拓扑序)
scc_list : Array[Array[String]]
/// 谓词 -> SCC 编号
scc_id : @hashmap.HashMap[String, Int]
}
///|
fn new_tarjan() -> Tarjan {
{
adj: @hashmap.HashMap([]),
neg: @hashset.HashSet([]),
idx_counter: 0,
index: @hashmap.HashMap([]),
lowlink: @hashmap.HashMap([]),
on_stack: @hashset.HashSet([]),
stack: [],
scc_list: [],
scc_id: @hashmap.HashMap([]),
}
}
///|
fn add_edge(t : Tarjan, from : String, to : String) -> Unit {
match t.adj.get(from) {
Some(list) => list.push(to)
None => t.adj.set(from, [to])
}
}
///|
fn Tarjan::strongconnect(self : Tarjan, v : String) -> Unit {
self.index.set(v, self.idx_counter)
self.lowlink.set(v, self.idx_counter)
self.idx_counter = self.idx_counter + 1
self.stack.push(v)
self.on_stack.add(v)
for w in self.adj.get_or_default(v, []) {
if !self.index.contains(w) {
self.strongconnect(w)
let lv = self.lowlink[v]
let lw = self.lowlink[w]
self.lowlink.set(v, if lw < lv { lw } else { lv })
} else if self.on_stack.contains(w) {
let lv = self.lowlink[v]
let iw = self.index[w]
self.lowlink.set(v, if iw < lv { iw } else { lv })
}
}
if self.lowlink[v] == self.index[v] {
let scc : Array[String] = []
while true {
match self.stack.pop() {
Some(w) => {
self.on_stack.remove(w)
scc.push(w)
if w == v {
break
}
}
None => break
}
}
let id = self.scc_list.length()
for node in scc {
self.scc_id.set(node, id)
}
self.scc_list.push(scc)
}
}
///|
/// 对规则集做分层。
///
/// 返回按求值顺序排列的分层(每层为一组规则);若程序不可分层
/// (否定依赖存在递归环),返回语义错误。
pub fn stratify(rules : Array[Rule]) -> Result[Array[Array[Rule]], DlError] {
let t = new_tarjan()
for rule in rules {
if !t.adj.contains(rule.head.pred) {
t.adj.set(rule.head.pred, [])
}
for item in rule.body {
match item {
Pos(a) => add_edge(t, rule.head.pred, a.pred)
Neg(a) => {
add_edge(t, rule.head.pred, a.pred)
t.neg.add((rule.head.pred, a.pred))
}
_ => ()
}
}
}
// 节点按字典序遍历,保证结果确定性
let nodes : Array[String] = []
for k in t.adj.keys() {
nodes.push(k)
}
nodes.sort()
for v in nodes {
if !t.index.contains(v) {
t.strongconnect(v)
}
}
// 检查否定边是否落在同一个 SCC 内
for pair in t.neg {
let (a, b) = pair
let ida = t.scc_id[a]
let idb = t.scc_id[b]
if ida == idb {
return Err(
SemanticError(
"程序不可分层:谓词 \{a} 通过否定依赖 \{b} 形成递归环(`not` 不能出现在递归规则中)",
),
)
}
}
// 求值顺序:Tarjan 的弹栈顺序即汇点优先(被依赖者先完成)。
// 依赖关系为 head -> body(head 依赖 body),因此汇点 = 被依赖的谓词,
// 必须先求值;直接按弹栈顺序(scc_list 原序)组织分层即可。
let strata : Array[Array[Rule]] = []
let n = t.scc_list.length()
let mut sid = 0
while sid < n {
let rules_in_scc : Array[Rule] = []
for rule in rules {
if t.scc_id[rule.head.pred] == sid {
rules_in_scc.push(rule)
}
}
if !rules_in_scc.is_empty() {
strata.push(rules_in_scc)
}
sid = sid + 1
}
Ok(strata)
}