logicaffeine_language/
pragmatics.rs

1//! Post-parse pragmatic inference transformations.
2//!
3//! This module applies pragmatic rules to the parsed logical representation,
4//! converting indirect speech acts to their illocutionary force:
5//!
6//! - Polite requests ("Can you X?") → Imperatives
7//! - Permission modals with addressee agent → Imperatives
8//!
9//! These transformations capture the pragmatic insight that modal questions
10//! directed at the addressee often function as requests, not information-seeking.
11
12use crate::ast::{LogicExpr, ModalDomain, ThematicRole, Term};
13use logicaffeine_base::Arena;
14use logicaffeine_base::Interner;
15
16/// Apply pragmatic transformations to convert indirect speech acts.
17pub fn apply_pragmatics<'a>(
18    expr: &'a LogicExpr<'a>,
19    expr_arena: &'a Arena<LogicExpr<'a>>,
20    interner: &Interner,
21) -> &'a LogicExpr<'a> {
22    match expr {
23        LogicExpr::Modal { vector, operand } => {
24            if vector.domain == ModalDomain::Alethic && vector.force > 0.0 && vector.force < 1.0 {
25                if is_addressee_agent(operand, interner) {
26                    return expr_arena.alloc(LogicExpr::Imperative { action: *operand });
27                }
28            }
29            expr
30        }
31        LogicExpr::Question { body, .. } => {
32            if let LogicExpr::Modal { vector, operand } = body {
33                if vector.domain == ModalDomain::Alethic && vector.force > 0.0 && vector.force < 1.0 {
34                    if is_addressee_agent(operand, interner) {
35                        return expr_arena.alloc(LogicExpr::Imperative { action: *operand });
36                    }
37                }
38            }
39            expr
40        }
41        LogicExpr::YesNoQuestion { body } => {
42            if let LogicExpr::Modal { vector, operand } = body {
43                if vector.domain == ModalDomain::Alethic && vector.force > 0.0 && vector.force < 1.0 {
44                    if is_addressee_agent(operand, interner) {
45                        return expr_arena.alloc(LogicExpr::Imperative { action: *operand });
46                    }
47                }
48            }
49            expr
50        }
51        _ => expr,
52    }
53}
54
55fn is_addressee_agent(expr: &LogicExpr, interner: &Interner) -> bool {
56    match expr {
57        LogicExpr::NeoEvent(data) => {
58            for (role, term) in data.roles.iter() {
59                if *role == ThematicRole::Agent {
60                    if let Term::Constant(sym) = term {
61                        let name = interner.resolve(*sym);
62                        if name == "Addressee" {
63                            return true;
64                        }
65                    }
66                }
67            }
68            false
69        }
70        LogicExpr::Predicate { args, .. } => {
71            if let Some(Term::Constant(sym)) = args.first() {
72                let name = interner.resolve(*sym);
73                return name == "Addressee";
74            }
75            false
76        }
77        _ => false,
78    }
79}