1use logicaffeine_language::ast::logic::{LogicExpr, QuantifierKind, TemporalOperator, BinaryTemporalOp, Term, ThematicRole};
8use logicaffeine_language::Interner;
9use logicaffeine_language::token::TokenType;
10use super::sva_to_verify::BoundedExpr;
11use super::hw_pipeline::SignalMap;
12use std::collections::{HashMap, HashSet};
13use logicaffeine_base::Symbol;
14
15pub struct FolTranslator<'a> {
17 interner: &'a Interner,
18 bound: u32,
19 world_map: HashMap<Symbol, u32>,
21 declarations: HashSet<String>,
23 signal_map: Option<&'a SignalMap>,
25 collapse_truth_predicates: bool,
28}
29
30impl<'a> FolTranslator<'a> {
31 pub fn new(interner: &'a Interner, bound: u32) -> Self {
32 Self {
33 interner,
34 bound,
35 world_map: HashMap::new(),
36 declarations: HashSet::new(),
37 signal_map: None,
38 collapse_truth_predicates: false,
39 }
40 }
41
42 pub fn set_collapse_truth_predicates(&mut self, collapse: bool) {
44 self.collapse_truth_predicates = collapse;
45 }
46
47 pub fn set_signal_map(&mut self, map: &'a SignalMap) {
49 self.signal_map = Some(map);
50 }
51
52 fn extract_accessibility_from_existential<'b>(
57 &self,
58 body: &'b LogicExpr<'b>,
59 quantified_var: Symbol,
60 ) -> Option<(Symbol, &'b LogicExpr<'b>, bool)> {
61 if let LogicExpr::BinaryOp { left, op, right } = body {
62 if matches!(op, TokenType::And) {
63 if let LogicExpr::Predicate { name, args, world: None } = *left {
64 let pred_name = self.interner.resolve(*name);
65 if pred_name == "Reachable_Temporal" || pred_name == "Accessible_Temporal" {
66 if args.len() >= 2 {
67 if let (Term::Variable(source), Term::Variable(target)) = (&args[0], &args[1]) {
68 if *target == quantified_var {
69 let strictly_future = pred_name == "Reachable_Temporal";
70 return Some((*source, right, strictly_future));
71 }
72 }
73 }
74 }
75 }
76 }
77 }
78 None
79 }
80
81 fn extract_accessibility_from_universal<'b>(
86 &self,
87 body: &'b LogicExpr<'b>,
88 quantified_var: Symbol,
89 ) -> Option<(Symbol, &'b LogicExpr<'b>, &'a str)> {
90 if let LogicExpr::BinaryOp { left, op, right } = body {
91 if matches!(op, TokenType::If | TokenType::Implies) {
92 if let LogicExpr::Predicate { name, args, world: None } = *left {
93 let pred_name = self.interner.resolve(*name);
94 if pred_name == "Accessible_Temporal"
95 || pred_name == "Reachable_Temporal"
96 || pred_name == "Next_Temporal"
97 {
98 if args.len() >= 2 {
99 if let (Term::Variable(source), Term::Variable(target)) = (&args[0], &args[1]) {
100 if *target == quantified_var {
101 return Some((*source, right, pred_name));
102 }
103 }
104 }
105 }
106 }
107 }
108 }
109 None
110 }
111
112 pub fn translate(&mut self, expr: &LogicExpr<'_>) -> BoundedExpr {
114 match expr {
115 LogicExpr::Predicate { name, args, world } => {
117 let pred_name = self.interner.resolve(*name).to_string();
118
119 if pred_name == "Accessible_Temporal"
121 || pred_name == "Reachable_Temporal"
122 || pred_name == "Next_Temporal"
123 {
124 if args.len() >= 2 {
126 if let (Term::Variable(source), Term::Variable(target)) = (&args[0], &args[1]) {
127 let source_t = self.world_map.get(source).copied().unwrap_or(0);
128 let target_t = self.world_map.get(target).copied().unwrap_or(0);
129
130 return match pred_name.as_str() {
131 "Accessible_Temporal" => BoundedExpr::Bool(target_t >= source_t),
132 "Reachable_Temporal" => BoundedExpr::Bool(target_t > source_t),
133 "Next_Temporal" => BoundedExpr::Bool(target_t == source_t + 1),
134 _ => BoundedExpr::Bool(true),
135 };
136 }
137 }
138 return BoundedExpr::Bool(true);
140 }
141
142 if let Some(w) = world {
144 let timestep = self.world_map.get(w).copied().unwrap_or(0);
145
146 if let Some(signal_map) = self.signal_map {
148 if let Some(sva_name) = signal_map.resolve(&pred_name) {
149 let var_name = format!("{}@{}", sva_name, timestep);
150 self.declarations.insert(var_name.clone());
151 return BoundedExpr::Var(var_name);
152 }
153 }
154
155 if args.is_empty() {
156 let var_name = format!("{}@{}", pred_name, timestep);
157 self.declarations.insert(var_name.clone());
158 return BoundedExpr::Var(var_name);
159 }
160 if let Some(arg) = args.first() {
162 let arg_name = self.term_to_string(arg);
163
164 if let Some(signal_map) = self.signal_map {
167 if let Some(sva_name) = signal_map.resolve(&arg_name) {
168 let var_name = format!("{}@{}", sva_name, timestep);
169 self.declarations.insert(var_name.clone());
170 return BoundedExpr::Var(var_name);
171 }
172 }
173
174 let var_name = format!("{}_{}_@{}", pred_name, arg_name, timestep);
175 self.declarations.insert(var_name.clone());
176 return BoundedExpr::Var(var_name);
177 }
178 }
179
180 let var_name = pred_name;
182 self.declarations.insert(var_name.clone());
183 BoundedExpr::Var(var_name)
184 }
185
186 LogicExpr::Quantifier { kind: QuantifierKind::Universal, variable, body, .. } => {
188 let var_name = self.interner.resolve(*variable).to_string();
189 if var_name.starts_with('w') {
190 if let Some((source_world, actual_body, pred_kind)) =
192 self.extract_accessibility_from_universal(body, *variable)
193 {
194 let source_t = self.world_map.get(&source_world).copied().unwrap_or(0);
195
196 if pred_kind == "Next_Temporal" {
198 let next_t = source_t + 1;
199 self.world_map.insert(*variable, next_t);
200 let step = self.translate(actual_body);
201 self.world_map.remove(variable);
202 return step;
203 }
204
205 let (start, end) = match pred_kind {
206 "Accessible_Temporal" => (source_t, source_t + self.bound),
207 "Reachable_Temporal" => (source_t + 1, source_t + 1 + self.bound),
208 _ => (0, self.bound),
209 };
210
211 let mut result: Option<BoundedExpr> = None;
212 for t in start..end {
213 self.world_map.insert(*variable, t);
214 let step = self.translate(actual_body);
215 result = Some(match result {
216 None => step,
217 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(step)),
218 });
219 }
220 self.world_map.remove(variable);
221 return result.unwrap_or(BoundedExpr::Bool(true));
222 }
223
224 let mut result: Option<BoundedExpr> = None;
226 for t in 0..self.bound {
227 self.world_map.insert(*variable, t);
228 let step = self.translate(body);
229 result = Some(match result {
230 None => step,
231 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(step)),
232 });
233 }
234 self.world_map.remove(variable);
235 result.unwrap_or(BoundedExpr::Bool(true))
236 } else {
237 if self.signal_map.is_some() || self.collapse_truth_predicates {
241 if let LogicExpr::BinaryOp { left, right, op } = body {
242 if matches!(op, TokenType::If | TokenType::Implies) {
243 if self.is_truth_expr(right) {
244 let restrictor = if self.signal_map.is_some() {
247 self.translate(left)
249 } else {
250 self.translate_predicate_bare(left)
252 };
253 if matches!(right, LogicExpr::UnaryOp { .. }) {
254 return BoundedExpr::Not(Box::new(restrictor));
255 }
256 return restrictor;
257 }
258 }
259 }
260 }
261 self.translate(body)
262 }
263 }
264
265 LogicExpr::Quantifier { kind: QuantifierKind::Existential, variable, body, .. } => {
267 let var_name = self.interner.resolve(*variable).to_string();
268 if var_name.starts_with('w') {
269 if let Some((source_world, actual_body, strictly_future)) =
271 self.extract_accessibility_from_existential(body, *variable)
272 {
273 let source_t = self.world_map.get(&source_world).copied().unwrap_or(0);
274 let start = if strictly_future { source_t + 1 } else { source_t };
275 let end = start + self.bound;
276
277 let mut result: Option<BoundedExpr> = None;
278 for t in start..end {
279 self.world_map.insert(*variable, t);
280 let step = self.translate(actual_body);
281 result = Some(match result {
282 None => step,
283 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(step)),
284 });
285 }
286 self.world_map.remove(variable);
287 return result.unwrap_or(BoundedExpr::Bool(false));
288 }
289
290 let mut result: Option<BoundedExpr> = None;
292 for t in 0..self.bound {
293 self.world_map.insert(*variable, t);
294 let step = self.translate(body);
295 result = Some(match result {
296 None => step,
297 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(step)),
298 });
299 }
300 self.world_map.remove(variable);
301 result.unwrap_or(BoundedExpr::Bool(false))
302 } else {
303 self.translate(body)
304 }
305 }
306
307 LogicExpr::BinaryOp { left, op, right } => {
309 let l = self.translate(left);
310 let r = self.translate(right);
311 match op {
312 TokenType::And => {
313 BoundedExpr::And(Box::new(l), Box::new(r))
314 }
315 TokenType::Or => {
316 BoundedExpr::Or(Box::new(l), Box::new(r))
317 }
318 TokenType::If
319 | TokenType::Implies => {
320 BoundedExpr::Implies(Box::new(l), Box::new(r))
321 }
322 _ => {
323 BoundedExpr::And(Box::new(l), Box::new(r))
325 }
326 }
327 }
328
329 LogicExpr::UnaryOp { operand, .. } => {
331 let inner = self.translate(operand);
332 BoundedExpr::Not(Box::new(inner))
333 }
334
335 LogicExpr::Temporal { operator, body } => {
337 match operator {
338 TemporalOperator::Always => {
339 let mut result: Option<BoundedExpr> = None;
341 for _t in 0..self.bound {
342 let step = self.translate(body);
344 result = Some(match result {
345 None => step,
346 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(step)),
347 });
348 }
349 result.unwrap_or(BoundedExpr::Bool(true))
350 }
351 TemporalOperator::Eventually => {
352 let mut result: Option<BoundedExpr> = None;
353 for _t in 0..self.bound {
354 let step = self.translate(body);
355 result = Some(match result {
356 None => step,
357 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(step)),
358 });
359 }
360 result.unwrap_or(BoundedExpr::Bool(false))
361 }
362 _ => self.translate(body),
363 }
364 }
365
366 LogicExpr::TemporalBinary { operator, left, right } => {
368 let l = self.translate(left);
369 let r = self.translate(right);
370 match operator {
371 BinaryTemporalOp::Until => {
372 self.unroll_until(&l, &r, 0)
373 }
374 BinaryTemporalOp::Release => {
375 self.unroll_release(&l, &r, 0)
376 }
377 BinaryTemporalOp::WeakUntil => {
378 let until = self.unroll_until(&l, &r, 0);
379 let always = self.unroll_always(&l, 0);
380 BoundedExpr::Or(Box::new(until), Box::new(always))
381 }
382 }
383 }
384
385 LogicExpr::Identity { left, right, .. } => {
387 let l = self.term_to_bounded(left);
388 let r = self.term_to_bounded(right);
389 BoundedExpr::Eq(Box::new(l), Box::new(r))
390 }
391
392 LogicExpr::NeoEvent(data) => {
394 let verb_name = self.interner.resolve(data.verb).to_string();
395 let timestep = data.world
396 .and_then(|w| self.world_map.get(&w).copied())
397 .unwrap_or(0);
398
399 let agent_name = data.roles.iter()
401 .find(|(role, _)| matches!(role, ThematicRole::Agent))
402 .map(|(_, term)| self.term_to_string(term));
403
404 if let Some(ref arg_name) = agent_name {
405 if let Some(signal_map) = self.signal_map {
406 if let Some(sva_name) = signal_map.resolve(arg_name) {
407 let var_name = format!("{}@{}", sva_name, timestep);
408 self.declarations.insert(var_name.clone());
409 return BoundedExpr::Var(var_name);
410 }
411 if let Some(sva_name) = signal_map.resolve(&verb_name) {
412 let var_name = format!("{}@{}", sva_name, timestep);
413 self.declarations.insert(var_name.clone());
414 return BoundedExpr::Var(var_name);
415 }
416 }
417 let var_name = format!("{}_{}_@{}", verb_name, arg_name, timestep);
418 self.declarations.insert(var_name.clone());
419 BoundedExpr::Var(var_name)
420 } else {
421 if let Some(signal_map) = self.signal_map {
422 if let Some(sva_name) = signal_map.resolve(&verb_name) {
423 let var_name = format!("{}@{}", sva_name, timestep);
424 self.declarations.insert(var_name.clone());
425 return BoundedExpr::Var(var_name);
426 }
427 }
428 let var_name = format!("{}@{}", verb_name, timestep);
429 self.declarations.insert(var_name.clone());
430 BoundedExpr::Var(var_name)
431 }
432 }
433
434 LogicExpr::Modal { operand, .. } => {
436 self.translate(operand)
437 }
438
439 _ => BoundedExpr::Bool(false),
442 }
443 }
444
445 pub fn translate_property(&mut self, expr: &LogicExpr<'_>) -> super::sva_to_verify::TranslateResult {
447 let expr_result = self.translate(expr);
448 let declarations: Vec<String> = self.declarations.iter().cloned().collect();
449 super::sva_to_verify::TranslateResult {
450 expr: expr_result,
451 declarations,
452 }
453 }
454
455 fn unroll_until(&self, phi: &BoundedExpr, psi: &BoundedExpr, depth: u32) -> BoundedExpr {
457 if depth >= self.bound {
458 psi.clone()
459 } else {
460 let rest = self.unroll_until(phi, psi, depth + 1);
461 BoundedExpr::Or(
462 Box::new(psi.clone()),
463 Box::new(BoundedExpr::And(
464 Box::new(phi.clone()),
465 Box::new(rest),
466 )),
467 )
468 }
469 }
470
471 fn unroll_release(&self, phi: &BoundedExpr, psi: &BoundedExpr, depth: u32) -> BoundedExpr {
473 if depth >= self.bound {
474 psi.clone()
475 } else {
476 let rest = self.unroll_release(phi, psi, depth + 1);
477 BoundedExpr::And(
478 Box::new(psi.clone()),
479 Box::new(BoundedExpr::Or(
480 Box::new(phi.clone()),
481 Box::new(rest),
482 )),
483 )
484 }
485 }
486
487 fn unroll_always(&self, phi: &BoundedExpr, depth: u32) -> BoundedExpr {
489 if depth >= self.bound {
490 phi.clone()
491 } else {
492 let rest = self.unroll_always(phi, depth + 1);
493 BoundedExpr::And(Box::new(phi.clone()), Box::new(rest))
494 }
495 }
496
497 fn is_truth_expr(&self, expr: &LogicExpr<'_>) -> bool {
502 match expr {
503 LogicExpr::Predicate { name, .. } => {
504 let pred_name = self.interner.resolve(*name).to_string();
505 is_truth_predicate(&pred_name)
506 }
507 LogicExpr::NeoEvent(data) => {
508 let verb_name = self.interner.resolve(data.verb).to_string();
509 is_truth_predicate(&verb_name)
510 }
511 LogicExpr::UnaryOp { operand, .. } => self.is_truth_expr(operand),
512 _ => false,
513 }
514 }
515
516 fn translate_predicate_bare(&mut self, expr: &LogicExpr<'_>) -> BoundedExpr {
519 match expr {
520 LogicExpr::Predicate { name, world, .. } => {
521 let pred_name = self.interner.resolve(*name).to_string();
522 let timestep = world
523 .and_then(|w| self.world_map.get(&w).copied())
524 .unwrap_or(0);
525 let var_name = format!("{}@{}", pred_name, timestep);
526 self.declarations.insert(var_name.clone());
527 BoundedExpr::Var(var_name)
528 }
529 _ => self.translate(expr),
530 }
531 }
532
533 fn term_to_string(&self, term: &Term<'_>) -> String {
534 match term {
535 Term::Constant(sym) | Term::Variable(sym) => {
536 self.interner.resolve(*sym).to_string()
537 }
538 Term::Function(sym, _) => self.interner.resolve(*sym).to_string(),
539 _ => "unknown".to_string(),
540 }
541 }
542
543 fn term_to_bounded(&self, term: &Term<'_>) -> BoundedExpr {
544 let name = self.term_to_string(term);
545 BoundedExpr::Var(name)
546 }
547}
548
549fn is_truth_predicate(name: &str) -> bool {
554 let lower = name.to_lowercase();
555 matches!(lower.as_str(),
556 "hold" | "holds" | "have" | "has" | "had"
557 | "valid" | "active" | "true" | "assert" | "asserted"
558 )
559}