1use kotoba_core::{types::*, ir::*};
4use kotoba_graph::prelude::*;
5use crate::rewrite::*;
6use kotoba_core::types::Result;
7
8#[derive(Debug)]
10pub struct RewriteEngine {
11 matcher: RuleMatcher,
12 applier: RuleApplier,
13}
14
15impl RewriteEngine {
16 pub fn new() -> Self {
17 Self {
18 matcher: RuleMatcher::new(),
19 applier: RuleApplier::new(),
20 }
21 }
22
23 pub fn match_rule(&self, graph: &GraphRef, rule: &RuleIR, catalog: &Catalog) -> Result<Vec<Match>> {
25 self.matcher.find_matches(graph, rule, catalog)
26 }
27
28 pub fn rewrite(&self, graph: &GraphRef, rule: &RuleIR, strategy: &StrategyIR) -> Result<Patch> {
30 match &strategy.strategy {
31 StrategyOp::Once { rule: rule_name } => {
32 self.apply_once(graph, rule, rule_name)
33 }
34 StrategyOp::Exhaust { rule: rule_name, order, measure } => {
35 self.apply_exhaust(graph, rule, rule_name, order, measure.as_deref())
36 }
37 StrategyOp::While { rule: rule_name, pred, order } => {
38 self.apply_while(graph, rule, rule_name, pred, order)
39 }
40 StrategyOp::Seq { strategies } => {
41 self.apply_sequence(graph, rule, strategies)
42 }
43 StrategyOp::Choice { strategies } => {
44 self.apply_choice(graph, rule, strategies)
45 }
46 StrategyOp::Priority { strategies } => {
47 self.apply_priority(graph, rule, strategies)
48 }
49 }
50 }
51
52 fn apply_once(&self, graph: &GraphRef, rule: &RuleIR, _rule_name: &str) -> Result<Patch> {
54 let matches = self.matcher.find_matches(graph, rule, &Catalog::empty())?;
55
56 if let Some(match_) = matches.into_iter().next() {
57 self.applier.apply_rule(graph, rule, &match_)
58 } else {
59 Ok(Patch::empty())
60 }
61 }
62
63 fn apply_exhaust(&self, graph: &GraphRef, rule: &RuleIR, _rule_name: &str,
65 order: &Order, _measure: Option<&str>) -> Result<Patch> {
66 let mut total_patch = Patch::empty();
67 let mut iteration = 0;
68 let max_iterations = 1000; loop {
71 let matches = self.matcher.find_matches(graph, rule, &Catalog::empty())?;
72
73 if matches.is_empty() || iteration >= max_iterations {
74 break;
75 }
76
77 let match_ = self.select_match(&matches, order)?;
79
80 let patch = self.applier.apply_rule(graph, rule, &match_)?;
81 total_patch = total_patch.merge(patch);
82
83 iteration += 1;
84 }
85
86 Ok(total_patch)
87 }
88
89 fn apply_while(&self, graph: &GraphRef, rule: &RuleIR, _rule_name: &str,
91 pred: &str, order: &Order) -> Result<Patch> {
92 let mut total_patch = Patch::empty();
93
94 loop {
95 let matches = self.matcher.find_matches(graph, rule, &Catalog::empty())?;
96
97 if matches.is_empty() {
98 break;
99 }
100
101 if !self.evaluate_predicate(pred, graph) {
103 break;
104 }
105
106 let match_ = self.select_match(&matches, order)?;
107 let patch = self.applier.apply_rule(graph, rule, &match_)?;
108 total_patch = total_patch.merge(patch);
109 }
110
111 Ok(total_patch)
112 }
113
114 fn apply_sequence(&self, graph: &GraphRef, rule: &RuleIR,
116 strategies: &[Box<StrategyOp>]) -> Result<Patch> {
117 let mut total_patch = Patch::empty();
118
119 for strategy in strategies {
120 let strategy_ir = StrategyIR {
121 strategy: *strategy.clone(),
122 };
123 let patch = self.rewrite(graph, rule, &strategy_ir)?;
124 total_patch = total_patch.merge(patch);
125 }
126
127 Ok(total_patch)
128 }
129
130 fn apply_choice(&self, graph: &GraphRef, rule: &RuleIR,
132 strategies: &[Box<StrategyOp>]) -> Result<Patch> {
133 for strategy in strategies {
134 let strategy_ir = StrategyIR {
135 strategy: *strategy.clone(),
136 };
137 let patch = self.rewrite(graph, rule, &strategy_ir)?;
138
139 if !patch.is_empty() {
140 return Ok(patch);
141 }
142 }
143
144 Ok(Patch::empty())
145 }
146
147 fn apply_priority(&self, graph: &GraphRef, rule: &RuleIR,
149 strategies: &[PrioritizedStrategy]) -> Result<Patch> {
150 let mut sorted_strategies = strategies.to_vec();
152 sorted_strategies.sort_by_key(|s| s.priority);
153
154 for prioritized in sorted_strategies {
155 let strategy_ir = StrategyIR {
156 strategy: *prioritized.strategy,
157 };
158 let patch = self.rewrite(graph, rule, &strategy_ir)?;
159
160 if !patch.is_empty() {
161 return Ok(patch);
162 }
163 }
164
165 Ok(Patch::empty())
166 }
167
168 fn select_match(&self, matches: &[Match], order: &Order) -> Result<Match> {
170 if matches.is_empty() {
171 return Err(KotobaError::Rewrite("No matches found".to_string()));
172 }
173
174 match order {
175 Order::TopDown => Ok(matches[0].clone()), Order::BottomUp => Ok(matches[matches.len() - 1].clone()), Order::Fair => Ok(matches[0].clone()), }
179 }
180
181 fn evaluate_predicate(&self, pred: &str, graph: &GraphRef) -> bool {
183 match pred {
185 "edge_count_nonincreasing" => {
186 graph.read().edge_count() <= 1000 }
189 "deg_ge" => {
190 true
192 }
193 _ => true, }
195 }
196}
197
198pub trait RewriteExterns {
200 fn deg_ge(&self, v: VertexId, k: u32) -> bool;
201 fn edge_count_nonincreasing(&self, g0: &GraphRef, g1: &GraphRef) -> bool;
202 fn custom_measure(&self, name: &str, args: &[Value]) -> f64;
203}