Skip to main content

cure_engine/
rule.rs

1//! 规则 trait 与编辑描述。
2//!
3//! 事务模型:`check`(只读 + 允许追加节点)产出 `Edit` 提案 →
4//! runner 计算成本降幅(必须 > 0)→ 原子应用。任何 edit 都必须严格降低成本,
5//! 这保证 fixed-point 收敛、杜绝 rewrite 振荡。
6
7use crate::cost;
8use crate::lang::Lang;
9use crate::walk::Walk;
10
11/// 一次结构编辑提案。
12#[derive(Debug)]
13pub enum Edit<L: Lang> {
14    /// 用 `with` 子树替换 `target` 节点(target 必须有父节点)。
15    Replace { target: L::Id, with: L::Id },
16    /// 删除一个节点(其父从 children 中移除该槽位)。
17    Delete { node: L::Id },
18    /// 对 `node` 自身的 children 做切片编辑。
19    Splice {
20        node: L::Id,
21        index: usize,
22        remove: usize,
23        insert: Vec<L::Id>,
24    },
25    /// 多个编辑按序原子应用。
26    /// 约束:前面的编辑不得使后面编辑的 (parent, index) 失效——
27    /// 先替换靠后的节点、后删除靠前的节点是安全顺序。
28    Multi(Vec<Edit<L>>),
29}
30
31impl<L: Lang> Edit<L> {
32    /// 应用本编辑会带来的成本降幅(严格 > 0 才允许生效)。
33    pub fn cost_reduction(&self, lang: &mut L) -> u64 {
34        match self {
35            Edit::Replace { target, with } => {
36                let a = cost::subtree_cost(lang, *target);
37                let b = cost::subtree_cost(lang, *with);
38                a.saturating_sub(b)
39            }
40            Edit::Delete { node } => cost::subtree_cost(lang, *node),
41            Edit::Splice {
42                node,
43                index,
44                remove,
45                insert,
46            } => {
47                let children = lang.children(*node);
48                let mut removed = 0u64;
49                for &c in &children[*index..(*index + remove).min(children.len())] {
50                    removed += cost::subtree_cost(lang, c);
51                }
52                let mut added = 0u64;
53                for &n in insert.iter() {
54                    added += cost::subtree_cost(lang, n);
55                }
56                removed.saturating_sub(added)
57            }
58            Edit::Multi(edits) => edits.iter().map(|e| e.cost_reduction(lang)).sum(),
59        }
60    }
61}
62
63/// 规则上下文:语言 AST 的可变访问 + 当前扫描的父/序号信息。
64pub struct RewriteCtx<'a, L: Lang> {
65    pub lang: &'a mut L,
66    pub(crate) walk: &'a Walk<L>,
67}
68
69impl<'a, L: Lang> RewriteCtx<'a, L> {
70    pub fn parent(&self, id: L::Id) -> Option<L::Id> {
71        self.walk.parent(id)
72    }
73    pub fn index(&self, id: L::Id) -> Option<usize> {
74        self.walk.index(id)
75    }
76    pub fn root(&self) -> L::Id {
77        self.walk.root
78    }
79}
80
81/// 一条简化规则。
82pub trait Rule<L: Lang> {
83    fn name(&self) -> &'static str;
84
85    /// 本规则关注的节点类别(**空 = 全部类别**)。pass 循环按类别建分派桶,
86    /// 无关节点直接跳过检查——"全部规则 × 每节点一次虚调用"是扫描主开销。
87    /// 声明必须覆盖 check() 入口的真实类别判定:多报只是浪费检查(正确),
88    /// 漏报会让规则失效(漏改写,回归测试会暴露)。
89    fn kinds(&self) -> &'static [crate::kind::NodeKind] {
90        &[]
91    }
92
93    /// 在节点 `id` 处检查是否可改写。
94    /// 契约:只读既有结构(可向 arena 追加新节点);结构变更只经 `Edit` 提案。
95    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>>;
96
97    /// 结构性规则:**豁免成本严格下降约束**。
98    /// 适用于"内联后暂时变贵、由后续折叠回本"的变换(如方法内联)。
99    /// 实现方必须自证终止(如:每次消费一类节点且不引入新的同类节点)。
100    fn structural(&self) -> bool {
101        false
102    }
103}
104
105/// runner 内部:用父表应用编辑。
106pub(crate) fn apply_edit<L: Lang>(
107    lang: &mut L,
108    walk: &Walk<L>,
109    edit: &Edit<L>,
110) -> Result<(), &'static str> {
111    match edit {
112        Edit::Replace { target, with } => {
113            let (parent, index) = walk
114                .parents
115                .get(target)
116                .copied()
117                .ok_or("cannot replace root")?;
118            lang.set_child(parent, index, *with);
119            Ok(())
120        }
121        Edit::Delete { node } => {
122            let (parent, index) = walk.parents.get(node).copied().ok_or("cannot delete root")?;
123            lang.remove_child(parent, index);
124            Ok(())
125        }
126        Edit::Splice {
127            node,
128            index,
129            remove,
130            insert,
131        } => {
132            lang.splice(*node, *index, *remove, insert.clone());
133            Ok(())
134        }
135        Edit::Multi(edits) => {
136            for e in edits {
137                apply_edit(lang, walk, e)?;
138            }
139            Ok(())
140        }
141    }
142}
143
144// ---------------------------------------------------------------------------
145// 同 pass 批量应用的支持设施:
146//
147// 正确性基石——**纯 Replace(含全 Replace 的 Multi)原位换孩子,不移动
148// 兄弟下标** ⇒ 快照的 (parent, index) 全程有效,可立即应用并继续扫描;
149// 含 Delete/Splice 的结构编辑会移动兄弟下标 ⇒ 延迟到 pass 末统一应用。
150//
151// 冲突域:每个编辑有「足迹」(它改动/删除的节点 + 其子树)。同 pass 内
152// 已应用(立即路径)与已排队(延迟路径)编辑的足迹之并 = touched 集合:
153//   - 扫描跳过 touched 节点(已脱离/将被删,提案无意义);
154//   - 新提案足迹与 touched 相交 → 放弃本轮(下一 pass 重新提案)。
155//
156// 延迟应用的对账:
157//   - Replace/Delete 在父的孩子表中按节点身份搜索定位(对下标漂移稳健);
158//   - Splice 要求被删区间仍等于提案时记录的节点序列,否则丢弃整个编辑
159//     (丢弃不影响成本单调性:已应用编辑各自严格降本);
160//   - Multi 原子性:先全部校验、后逐元素应用。
161// 排序约定:Multi 内同父的 Splice 应先于 Delete/低下标编辑(既有规则
162// 均遵守,见 CFF/LocalProp 注释);跨编辑按 (parent, 首个结构位置) 降序
163// 应用,最小化 Splice 漂移。
164// ---------------------------------------------------------------------------
165
166/// 纯 Replace 判定。
167pub(crate) fn is_replace_only<L: Lang>(edit: &Edit<L>) -> bool {
168    match edit {
169        Edit::Replace { .. } => true,
170        Edit::Multi(es) => es.iter().all(|e| matches!(e, Edit::Replace { .. })),
171        _ => false,
172    }
173}
174
175/// 编辑足迹:将被改动/删除的节点(含子树),提案时快照。
176pub(crate) fn collect_footprint<L: Lang>(lang: &L, edit: &Edit<L>, out: &mut Vec<L::Id>) {
177    match edit {
178        Edit::Replace { target, .. } => collect_subtree(lang, *target, out),
179        Edit::Delete { node } => collect_subtree(lang, *node, out),
180        Edit::Splice {
181            node, index, remove, ..
182        } => {
183            let ch = lang.children(*node);
184            if *index <= ch.len() && *index + *remove <= ch.len() {
185                for &c in &ch[*index..*index + *remove] {
186                    collect_subtree(lang, c, out);
187                }
188            }
189        }
190        Edit::Multi(es) => {
191            for e in es {
192                collect_footprint(lang, e, out);
193            }
194        }
195    }
196}
197
198fn collect_subtree<L: Lang>(lang: &L, id: L::Id, out: &mut Vec<L::Id>) {
199    let mut stack = vec![id];
200    while let Some(n) = stack.pop() {
201        out.push(n);
202        for &c in lang.children(n) {
203            stack.push(c);
204        }
205    }
206}
207
208/// Splice 校验数据:提案时被删区间的孩子节点序列(按编辑树先序展平)。
209pub(crate) fn collect_splice_expectations<L: Lang>(
210    lang: &L,
211    edit: &Edit<L>,
212    out: &mut Vec<Vec<L::Id>>,
213) {
214    match edit {
215        Edit::Splice {
216            node, index, remove, ..
217        } => {
218            let ch = lang.children(*node);
219            if *index <= ch.len() && *index + *remove <= ch.len() {
220                out.push(ch[*index..*index + *remove].to_vec());
221            } else {
222                // 越界占位(保持展平顺序与编辑树对应)
223                out.push(Vec::new());
224            }
225        }
226        Edit::Multi(es) => {
227            for e in es {
228                collect_splice_expectations(lang, e, out);
229            }
230        }
231        _ => {}
232    }
233}
234
235/// 延迟路径应用前校验(只读)。`expects` 按消费推进。
236/// Replace/Delete:目标仍在其快照父的孩子表中(节点身份匹配,无视下标)。
237/// Splice:被删区间仍等于提案时记录的序列。
238pub(crate) fn verify_deferred<L: Lang>(
239    lang: &L,
240    snap: &Walk<L>,
241    edit: &Edit<L>,
242    expects: &mut &[Vec<L::Id>],
243) -> bool {
244    match edit {
245        Edit::Replace { target, .. } => match snap.parents.get(target) {
246            Some((p, _)) => lang.children(*p).contains(target),
247            None => false,
248        },
249        Edit::Delete { node } => match snap.parents.get(node) {
250            Some((p, _)) => lang.children(*p).contains(node),
251            None => false,
252        },
253        Edit::Splice {
254            node, index, remove, ..
255        } => {
256            let Some(exp) = expects.first() else {
257                return false;
258            };
259            *expects = &expects[1..];
260            let ch = lang.children(*node);
261            *index <= ch.len()
262                && *index + *remove <= ch.len()
263                && &ch[*index..*index + *remove] == exp
264        }
265        Edit::Multi(es) => es.iter().all(|e| verify_deferred(lang, snap, e, expects)),
266    }
267}
268
269/// 延迟路径应用(校验已通过后)。Replace/Delete 按节点身份搜索定位;
270/// Splice 用快照下标(校验已确认区间一致)。
271pub(crate) fn apply_deferred<L: Lang>(lang: &mut L, snap: &Walk<L>, edit: &Edit<L>) {
272    match edit {
273        Edit::Replace { target, with } => {
274            if let Some((p, _)) = snap.parents.get(target) {
275                if let Some(i) = lang.children(*p).iter().position(|&c| c == *target) {
276                    lang.set_child(*p, i, *with);
277                }
278            }
279        }
280        Edit::Delete { node } => {
281            if let Some((p, _)) = snap.parents.get(node) {
282                if let Some(i) = lang.children(*p).iter().position(|&c| c == *node) {
283                    lang.remove_child(*p, i);
284                }
285            }
286        }
287        Edit::Splice {
288            node, index, remove, insert,
289        } => {
290            lang.splice(*node, *index, *remove, insert.clone());
291        }
292        Edit::Multi(es) => {
293            for e in es {
294                apply_deferred(lang, snap, e);
295            }
296        }
297    }
298}
299
300/// 结构编辑的排序键:(首个结构元素的父, 位置)。降序应用 ⇒ 同父先动
301/// 高下标区间,低下标编辑的快照下标不被移动。
302pub(crate) fn structural_sort_key<L: Lang>(snap: &Walk<L>, edit: &Edit<L>) -> (L::Id, usize) {
303    fn first_pos<L: Lang>(snap: &Walk<L>, e: &Edit<L>) -> Option<(L::Id, usize)> {
304        match e {
305            Edit::Splice { node, index, .. } => Some((*node, *index)),
306            Edit::Delete { node } => snap
307                .parents
308                .get(node)
309                .map(|(p, i)| (*p, *i)),
310            Edit::Replace { target, .. } => snap
311                .parents
312                .get(target)
313                .map(|(p, i)| (*p, *i)),
314            Edit::Multi(es) => es.first().and_then(|x| first_pos(snap, x)),
315        }
316    }
317    first_pos(snap, edit).unwrap_or((snap.root, 0))
318}