1use mangle_ast as ast;
16use rustc_hash::{FxHashMap, FxHashSet};
17
18pub fn rewrite_unit<'a>(arena: &'a ast::Arena, unit: &'a ast::Unit<'a>) -> ast::Unit<'a> {
20 let (pkg_name, used_pkgs) = find_package_info(arena, unit);
21
22 if pkg_name.is_empty() {
23 return ast::Unit {
24 decls: unit.decls,
25 clauses: unit.clauses,
26 };
27 }
28
29 let defined_preds = find_defined_preds(unit);
30 let mut renamer = Renamer {
31 arena,
32 pkg_name,
33 used_pkgs,
34 defined_preds,
35 cache: FxHashMap::default(),
36 };
37
38 let mut new_decls = Vec::with_capacity(unit.decls.len());
39 for &decl in unit.decls {
40 if let Some(new_decl) = renamer.rewrite_decl(decl) {
41 new_decls.push(new_decl);
42 }
43 }
44
45 let mut new_clauses = Vec::with_capacity(unit.clauses.len());
46 for &clause in unit.clauses {
47 if let Some(new_clause) = renamer.rewrite_clause(clause) {
48 new_clauses.push(new_clause);
49 }
50 }
51
52 ast::Unit {
53 decls: arena.alloc_slice_copy(&new_decls),
54 clauses: arena.alloc_slice_copy(&new_clauses),
55 }
56}
57
58fn find_package_info<'a>(
59 arena: &'a ast::Arena,
60 unit: &'a ast::Unit<'a>,
61) -> (&'a str, FxHashSet<&'a str>) {
62 let mut pkg_name = "";
63 let mut used_pkgs = FxHashSet::default();
64
65 for &decl in unit.decls {
66 let pred_name = arena.predicate_name(decl.atom.sym).unwrap_or("");
67 if pred_name == "Package" {
68 if let Some(desc) = find_name_desc(arena, decl.descr) {
69 pkg_name = desc;
70 }
71 } else if pred_name == "Use"
72 && let Some(desc) = find_name_desc(arena, decl.descr)
73 {
74 used_pkgs.insert(desc);
75 }
76 }
77 (pkg_name, used_pkgs)
78}
79
80fn find_name_desc<'a>(arena: &'a ast::Arena, descr: &'a [&'a ast::Atom<'a>]) -> Option<&'a str> {
81 for &atom in descr {
82 if arena.predicate_name(atom.sym).unwrap_or("") == "name"
83 && let Some(&ast::BaseTerm::Const(ast::Const::String(s))) = atom.args.first().copied()
84 {
85 return Some(s);
86 }
87 }
88 None
89}
90
91fn find_defined_preds(unit: &ast::Unit) -> FxHashSet<ast::PredicateIndex> {
92 let mut defined = FxHashSet::default();
93 for &decl in unit.decls {
94 defined.insert(decl.atom.sym);
95 }
96 for &clause in unit.clauses {
97 defined.insert(clause.head.sym);
98 }
99 defined
100}
101
102struct Renamer<'a> {
103 arena: &'a ast::Arena,
104 pkg_name: &'a str,
105 used_pkgs: FxHashSet<&'a str>,
106 defined_preds: FxHashSet<ast::PredicateIndex>,
107 cache: FxHashMap<ast::PredicateIndex, ast::PredicateIndex>,
108}
109
110impl<'a> Renamer<'a> {
111 fn rename_pred(&mut self, sym: ast::PredicateIndex) -> Option<ast::PredicateIndex> {
112 if let Some(&new_sym) = self.cache.get(&sym) {
113 return Some(new_sym);
114 }
115
116 let name = self.arena.predicate_name(sym)?;
117
118 if name == "Package" || name == "Use" {
120 self.cache.insert(sym, sym);
121 return Some(sym);
122 }
123
124 if self.defined_preds.contains(&sym) {
125 let new_name = format!("{}.{}", self.pkg_name, name);
126 let new_sym = self
127 .arena
128 .predicate_sym(&new_name, self.arena.predicate_arity(sym));
129 self.cache.insert(sym, new_sym);
130 return Some(new_sym);
131 }
132
133 if let Some(dot_idx) = name.rfind('.') {
135 let prefix = &name[..dot_idx];
136 if self.used_pkgs.contains(prefix) {
137 self.cache.insert(sym, sym);
139 return Some(sym);
140 }
141 }
142
143 self.cache.insert(sym, sym);
145 Some(sym)
146 }
147
148 fn rewrite_decl(&mut self, decl: &'a ast::Decl<'a>) -> Option<&'a ast::Decl<'a>> {
149 let pred_name = self.arena.predicate_name(decl.atom.sym).unwrap_or("");
150 if pred_name == "Package" || pred_name == "Use" {
151 return None;
153 }
154
155 let new_atom = self.rewrite_atom(decl.atom)?;
156
157 let bounds = if let Some(bs) = decl.bounds {
159 let mut new_bounds = Vec::new();
160 for &b in bs {
161 let new_base_terms: Vec<&ast::BaseTerm> = b
162 .base_terms
163 .iter()
164 .map(|&t| {
165 if let ast::BaseTerm::Const(ast::Const::Name(name_idx)) = t {
167 let name = self.arena.lookup_name(*name_idx).unwrap_or("");
168 if let Some(pred_idx) = self.arena.lookup_predicate_sym(*name_idx)
170 && self.defined_preds.contains(&pred_idx)
171 {
172 let new_name = format!("{}.{}", self.pkg_name, name);
173 let new_name_idx = self.arena.intern(&new_name);
174 return &*self
175 .arena
176 .alloc(ast::BaseTerm::Const(ast::Const::Name(new_name_idx)));
177 }
178 }
179 t
180 })
181 .collect();
182 new_bounds.push(&*self.arena.alloc(ast::BoundDecl {
183 base_terms: self.arena.alloc_slice_copy(&new_base_terms),
184 }));
185 }
186 Some(self.arena.alloc_slice_copy(&new_bounds) as &'a [&'a ast::BoundDecl<'a>])
187 } else {
188 None
189 };
190
191 Some(self.arena.alloc(ast::Decl {
192 atom: new_atom,
193 descr: decl.descr, bounds,
195 constraints: decl.constraints,
196 is_temporal: decl.is_temporal,
197 }))
198 }
199
200 fn rewrite_clause(&mut self, clause: &'a ast::Clause<'a>) -> Option<&'a ast::Clause<'a>> {
201 let head = self.rewrite_atom(clause.head)?;
202 let mut premises = Vec::new();
203 for &premise in clause.premises {
204 match premise {
205 ast::Term::Atom(a) => {
206 premises.push(&*self.arena.alloc(ast::Term::Atom(self.rewrite_atom(a)?)));
207 }
208 ast::Term::NegAtom(a) => {
209 premises.push(&*self.arena.alloc(ast::Term::NegAtom(self.rewrite_atom(a)?)));
210 }
211 ast::Term::TemporalAtom(a, interval) => {
212 premises.push(
213 &*self
214 .arena
215 .alloc(ast::Term::TemporalAtom(self.rewrite_atom(a)?, *interval)),
216 );
217 }
218 _ => premises.push(premise),
219 }
220 }
221
222 Some(self.arena.alloc(ast::Clause {
223 head,
224 head_time: clause.head_time,
225 premises: self.arena.alloc_slice_copy(&premises),
226 transform: clause.transform,
227 }))
228 }
229
230 fn rewrite_atom(&mut self, atom: &'a ast::Atom<'a>) -> Option<&'a ast::Atom<'a>> {
231 let new_sym = self.rename_pred(atom.sym)?;
232 Some(self.arena.atom(new_sym, atom.args))
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use googletest::prelude::*;
240
241 #[test]
242 fn test_rename_simple() {
243 let arena = ast::Arena::new_with_global_interner();
244
245 let pkg_sym = arena.predicate_sym("Package", Some(0));
250 let name_sym = arena.predicate_sym("name", Some(1));
251 let pkg_name = arena.alloc(ast::BaseTerm::Const(ast::Const::String("pkg")));
252 let pkg_decl = arena.alloc(ast::Decl {
253 atom: arena.atom(pkg_sym, &[]),
254 descr: arena.alloc_slice_copy(&[arena.atom(name_sym, &[pkg_name])]),
255 bounds: None,
256 constraints: None,
257 is_temporal: false,
258 });
259
260 let foo_sym = arena.predicate_sym("foo", Some(1));
261 let bar_sym = arena.predicate_sym("bar", Some(1));
262 let var_x = arena.variable("X");
263 let const_1 = arena.const_(ast::Const::Number(1));
264
265 let clause1 = arena.alloc(ast::Clause {
266 head: arena.atom(foo_sym, &[var_x]),
267 head_time: None,
268 premises: arena
269 .alloc_slice_copy(&[arena.alloc(ast::Term::Atom(arena.atom(bar_sym, &[var_x])))]),
270 transform: &[],
271 });
272
273 let clause2 = arena.alloc(ast::Clause {
274 head: arena.atom(bar_sym, &[const_1]),
275 head_time: None,
276 premises: &[],
277 transform: &[],
278 });
279
280 let unit = ast::Unit {
281 decls: arena.alloc_slice_copy(&[pkg_decl]),
282 clauses: arena.alloc_slice_copy(&[clause1, clause2]),
283 };
284
285 let new_unit = rewrite_unit(&arena, &unit);
286
287 assert_that!(new_unit.decls.len(), eq(0));
289 assert_that!(new_unit.clauses.len(), eq(2));
290
291 let c1 = new_unit.clauses[0];
292 let c2 = new_unit.clauses[1];
293
294 let head_name = arena.predicate_name(c1.head.sym).unwrap();
296 assert_that!(head_name, eq("pkg.foo"));
297
298 if let ast::Term::Atom(a) = c1.premises[0] {
300 let p_name = arena.predicate_name(a.sym).unwrap();
301 assert_that!(p_name, eq("pkg.bar"));
302 } else {
303 panic!("Expected Atom premise");
304 }
305
306 let head_name_2 = arena.predicate_name(c2.head.sym).unwrap();
308 assert_that!(head_name_2, eq("pkg.bar"));
309 }
310
311 #[test]
312 fn test_rename_with_use() {
313 let arena = ast::Arena::new_with_global_interner();
314
315 let pkg_sym = arena.predicate_sym("Package", Some(0));
320 let use_sym = arena.predicate_sym("Use", Some(0));
321 let name_sym = arena.predicate_sym("name", Some(1));
322
323 let pkg_name = arena.alloc(ast::BaseTerm::Const(ast::Const::String("pkg")));
324 let pkg_decl = arena.alloc(ast::Decl {
325 atom: arena.atom(pkg_sym, &[]),
326 descr: arena.alloc_slice_copy(&[arena.atom(name_sym, &[pkg_name])]),
327 bounds: None,
328 constraints: None,
329 is_temporal: false,
330 });
331
332 let other_name = arena.alloc(ast::BaseTerm::Const(ast::Const::String("other")));
333 let use_decl = arena.alloc(ast::Decl {
334 atom: arena.atom(use_sym, &[]),
335 descr: arena.alloc_slice_copy(&[arena.atom(name_sym, &[other_name])]),
336 bounds: None,
337 constraints: None,
338 is_temporal: false,
339 });
340
341 let foo_sym = arena.predicate_sym("foo", Some(1));
342 let other_bar_sym = arena.predicate_sym("other.bar", Some(1));
343 let var_x = arena.variable("X");
344
345 let clause1 = arena.alloc(ast::Clause {
346 head: arena.atom(foo_sym, &[var_x]),
347 head_time: None,
348 premises: arena.alloc_slice_copy(&[
349 arena.alloc(ast::Term::Atom(arena.atom(other_bar_sym, &[var_x])))
350 ]),
351 transform: &[],
352 });
353
354 let unit = ast::Unit {
355 decls: arena.alloc_slice_copy(&[pkg_decl, use_decl]),
356 clauses: arena.alloc_slice_copy(&[clause1]),
357 };
358
359 let new_unit = rewrite_unit(&arena, &unit);
360
361 assert_that!(new_unit.clauses.len(), eq(1));
362 let c1 = new_unit.clauses[0];
363
364 let head_name = arena.predicate_name(c1.head.sym).unwrap();
366 assert_that!(head_name, eq("pkg.foo"));
367
368 if let ast::Term::Atom(a) = c1.premises[0] {
370 let p_name = arena.predicate_name(a.sym).unwrap();
371 assert_that!(p_name, eq("other.bar"));
372 } else {
373 panic!("Expected Atom premise");
374 }
375 }
376
377 fn make_pkg_decl<'a>(arena: &'a ast::Arena, name: &str) -> &'a ast::Decl<'a> {
378 let pkg_sym = arena.predicate_sym("Package", Some(0));
379 let name_sym = arena.predicate_sym("name", Some(1));
380 let pkg_name = arena.alloc(ast::BaseTerm::Const(ast::Const::String(
381 arena.alloc_str(name),
382 )));
383 arena.alloc(ast::Decl {
384 atom: arena.atom(pkg_sym, &[]),
385 descr: arena.alloc_slice_copy(&[arena.atom(name_sym, &[pkg_name])]),
386 bounds: None,
387 constraints: None,
388 is_temporal: false,
389 })
390 }
391
392 #[test]
393 fn test_go_case_no_package() {
394 let arena = ast::Arena::new_with_global_interner();
396 let clause = arena.alloc(ast::Clause {
397 head: arena.atom(arena.predicate_sym("clause_defined_here", None), &[]),
398 head_time: None,
399 premises: arena.alloc_slice_copy(&[arena.alloc(ast::Term::Atom(
400 arena.atom(arena.predicate_sym("other_clause", None), &[]),
401 ))]),
402 transform: &[],
403 });
404 let unit = ast::Unit {
405 decls: &[],
406 clauses: arena.alloc_slice_copy(&[clause]),
407 };
408 let new_unit = rewrite_unit(&arena, &unit);
409 let head = arena.predicate_name(new_unit.clauses[0].head.sym).unwrap();
410 assert_that!(head, eq("clause_defined_here"));
411 if let ast::Term::Atom(a) = new_unit.clauses[0].premises[0] {
412 let p = arena.predicate_name(a.sym).unwrap();
413 assert_that!(p, eq("other_clause"));
414 }
415 }
416
417 #[test]
418 fn test_go_case_external_refs() {
419 let arena = ast::Arena::new_with_global_interner();
421 let pkg_decl = make_pkg_decl(&arena, "foo.bar");
423
424 let clause = arena.alloc(ast::Clause {
427 head: arena.atom(arena.predicate_sym("clause_defined_here", None), &[]),
428 head_time: None,
429 premises: arena.alloc_slice_copy(&[arena.alloc(ast::Term::Atom(
430 arena.atom(arena.predicate_sym("other_clause", None), &[]),
431 ))]),
432 transform: &[],
433 });
434
435 let unit = ast::Unit {
436 decls: arena.alloc_slice_copy(&[pkg_decl]),
437 clauses: arena.alloc_slice_copy(&[clause]),
438 };
439 let new_unit = rewrite_unit(&arena, &unit);
440
441 let head = arena.predicate_name(new_unit.clauses[0].head.sym).unwrap();
443 assert_that!(head, eq("foo.bar.clause_defined_here"));
444
445 if let ast::Term::Atom(a) = new_unit.clauses[0].premises[0] {
447 let p = arena.predicate_name(a.sym).unwrap();
448 assert_that!(p, eq("other_clause"));
449 }
450 }
451
452 #[test]
453 fn test_go_case_rewritten_local() {
454 let arena = ast::Arena::new_with_global_interner();
456 let pkg_decl = make_pkg_decl(&arena, "foo.bar");
457
458 let defined_sym = arena.predicate_sym("clause_defined_here", None);
459 let other_sym = arena.predicate_sym("other_clause", None);
460
461 let clause1 = arena.alloc(ast::Clause {
463 head: arena.atom(other_sym, &[]),
464 head_time: None,
465 premises: &[],
466 transform: &[],
467 });
468
469 let clause2 = arena.alloc(ast::Clause {
471 head: arena.atom(defined_sym, &[]),
472 head_time: None,
473 premises: arena
474 .alloc_slice_copy(&[arena.alloc(ast::Term::Atom(arena.atom(other_sym, &[])))]),
475 transform: &[],
476 });
477
478 let unit = ast::Unit {
479 decls: arena.alloc_slice_copy(&[pkg_decl]),
480 clauses: arena.alloc_slice_copy(&[clause1, clause2]),
481 };
482 let new_unit = rewrite_unit(&arena, &unit);
483
484 let h1 = arena.predicate_name(new_unit.clauses[0].head.sym).unwrap();
486 assert_that!(h1, eq("foo.bar.other_clause"));
487
488 let h2 = arena.predicate_name(new_unit.clauses[1].head.sym).unwrap();
490 assert_that!(h2, eq("foo.bar.clause_defined_here"));
491
492 if let ast::Term::Atom(a) = new_unit.clauses[1].premises[0] {
494 let p = arena.predicate_name(a.sym).unwrap();
495 assert_that!(p, eq("foo.bar.other_clause"));
496 }
497 }
498
499 #[test]
500 fn test_go_case_negation() {
501 let arena = ast::Arena::new_with_global_interner();
503 let pkg_decl = make_pkg_decl(&arena, "foo.bar");
504
505 let defined_sym = arena.predicate_sym("clause_defined_here", None);
506 let other_sym = arena.predicate_sym("other_clause", None);
507
508 let clause1 = arena.alloc(ast::Clause {
510 head: arena.atom(other_sym, &[]),
511 head_time: None,
512 premises: &[],
513 transform: &[],
514 });
515
516 let clause2 = arena.alloc(ast::Clause {
518 head: arena.atom(defined_sym, &[]),
519 head_time: None,
520 premises: arena
521 .alloc_slice_copy(&[arena.alloc(ast::Term::NegAtom(arena.atom(other_sym, &[])))]),
522 transform: &[],
523 });
524
525 let unit = ast::Unit {
526 decls: arena.alloc_slice_copy(&[pkg_decl]),
527 clauses: arena.alloc_slice_copy(&[clause1, clause2]),
528 };
529 let new_unit = rewrite_unit(&arena, &unit);
530
531 if let ast::Term::NegAtom(a) = new_unit.clauses[1].premises[0] {
532 let p = arena.predicate_name(a.sym).unwrap();
533 assert_that!(p, eq("foo.bar.other_clause"));
534 } else {
535 panic!("Expected NegAtom");
536 }
537 }
538
539 #[test]
540 fn test_go_case_decl_only() {
541 let arena = ast::Arena::new_with_global_interner();
543 let pkg_decl = make_pkg_decl(&arena, "foo.bar");
544
545 let clause_sym = arena.predicate_sym("clause", None);
546 let decl_sym = arena.predicate_sym("from_decl", None);
547
548 let decl = arena.alloc(ast::Decl {
550 atom: arena.atom(decl_sym, &[]),
551 descr: &[],
552 bounds: None,
553 constraints: None,
554 is_temporal: false,
555 });
556
557 let clause = arena.alloc(ast::Clause {
559 head: arena.atom(clause_sym, &[]),
560 head_time: None,
561 premises: arena
562 .alloc_slice_copy(&[arena.alloc(ast::Term::Atom(arena.atom(decl_sym, &[])))]),
563 transform: &[],
564 });
565
566 let unit = ast::Unit {
567 decls: arena.alloc_slice_copy(&[pkg_decl, decl]),
568 clauses: arena.alloc_slice_copy(&[clause]),
569 };
570 let new_unit = rewrite_unit(&arena, &unit);
571
572 let h = arena.predicate_name(new_unit.clauses[0].head.sym).unwrap();
574 assert_that!(h, eq("foo.bar.clause"));
575
576 if let ast::Term::Atom(a) = new_unit.clauses[0].premises[0] {
578 let p = arena.predicate_name(a.sym).unwrap();
579 assert_that!(p, eq("foo.bar.from_decl"));
580 }
581
582 let d_name = arena.predicate_name(new_unit.decls[0].atom.sym).unwrap();
584 assert_that!(d_name, eq("foo.bar.from_decl"));
585 }
586}