Skip to main content

logicaffeine_language/ast/
logic.rs

1//! Logic expression AST types for first-order logic with modal and event extensions.
2//!
3//! This module defines the core logical expression types including:
4//!
5//! - **[`LogicExpr`]**: The main expression enum with all logical constructs
6//! - **[`Term`]**: Terms (constants, variables, function applications)
7//! - **[`NounPhrase`]**: Parsed noun phrase structure
8//! - **Semantic types**: Montague-style type markers
9//! - **Event roles**: Neo-Davidsonian thematic roles (Agent, Theme, Goal, etc.)
10//! - **Modal vectors**: Kripke semantics parameters (domain, flavor, force)
11//! - **Temporal operators**: Past, future, perfect, progressive
12//!
13//! All types use arena allocation with the `'a` lifetime parameter.
14
15use logicaffeine_base::Arena;
16use logicaffeine_base::Symbol;
17use crate::lexicon::Definiteness;
18use crate::token::TokenType;
19
20// ═══════════════════════════════════════════════════════════════════
21// Semantic Types (Montague Grammar)
22// ═══════════════════════════════════════════════════════════════════
23
24/// Montague semantic types for compositional interpretation.
25///
26/// These types classify expressions according to their denotation in
27/// a model-theoretic semantics, following Montague's "Universal Grammar".
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum LogicalType {
30    /// Type `e`: Individuals (entities) like "John" or "the ball".
31    Entity,
32    /// Type `t`: Truth values (propositions) like "John runs".
33    TruthValue,
34    /// Type `<e,t>`: Properties (one-place predicates) like "is a unicorn".
35    Property,
36    /// Type `<<e,t>,t>`: Generalized quantifiers like "every man" or "a woman".
37    Quantifier,
38}
39
40// ═══════════════════════════════════════════════════════════════════
41// Degree Semantics (Prover-Ready Number System)
42// ═══════════════════════════════════════════════════════════════════
43
44/// Physical dimension for degree semantics and unit tracking.
45///
46/// Used with [`NumberKind`] to enable dimensional analysis and prevent
47/// nonsensical comparisons (e.g., adding meters to seconds).
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum Dimension {
50    /// Spatial extent (meters, feet, inches).
51    Length,
52    /// Temporal duration (seconds, minutes, hours).
53    Time,
54    /// Mass (kilograms, pounds).
55    Weight,
56    /// Thermal measure (Celsius, Fahrenheit, Kelvin).
57    Temperature,
58    /// Count of discrete items.
59    Cardinality,
60}
61
62/// Numeric literal representation for degree semantics.
63///
64/// Supports exact integers, floating-point reals, and symbolic constants
65/// (e.g., π, e) for prover integration.
66#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum NumberKind {
68    /// Floating-point real number (e.g., 3.14, 0.5).
69    Real(f64),
70    /// Exact integer (e.g., 42, -1, 0).
71    Integer(i64),
72    /// Symbolic constant (e.g., π, e, ∞).
73    Symbolic(Symbol),
74}
75
76// ═══════════════════════════════════════════════════════════════════
77// First-Order Logic Types (FOL Upgrade)
78// ═══════════════════════════════════════════════════════════════════
79
80/// First-order logic term representing entities or values.
81///
82/// Terms denote individuals, groups, or computed values in the domain
83/// of discourse. They serve as arguments to predicates.
84#[derive(Debug, Clone, Copy)]
85pub enum Term<'a> {
86    /// Named individual constant (e.g., `john`, `paris`).
87    Constant(Symbol),
88    /// Bound or free variable (e.g., `x`, `y`).
89    Variable(Symbol),
90    /// Function application: `f(t1, t2, ...)` (e.g., `mother(john)`).
91    Function(Symbol, &'a [Term<'a>]),
92    /// Plural group for collective readings (e.g., `john ⊕ mary`).
93    Group(&'a [Term<'a>]),
94    /// Possessive construction: `john's book` → `Poss(john, book)`.
95    Possessed { possessor: &'a Term<'a>, possessed: Symbol },
96    /// Definite description σ-term: `σx.P(x)` ("the unique x such that P").
97    Sigma(Symbol),
98    /// Intensional term (Montague up-arrow `^P`) for de dicto readings.
99    Intension(Symbol),
100    /// Kind term (`^Kind`) — a Carlson-style kind-denoting entity, distinct
101    /// from a Montague intension. Used for kind reference ("the dodo is extinct")
102    /// and as the base of a kind-level relational adjective ("dental procedure"
103    /// → `Pertains(x, ^Tooth)`; McNally & Boleda 2004, predicates of kinds).
104    Kind(Symbol),
105    /// Sentential complement (embedded clause as propositional argument).
106    Proposition(&'a LogicExpr<'a>),
107    /// Numeric value with optional unit and dimension.
108    Value {
109        kind: NumberKind,
110        unit: Option<Symbol>,
111        dimension: Option<Dimension>,
112    },
113}
114
115/// Degree-comparison relation for [`LogicExpr::Comparative`].
116///
117/// `Greater` is the strict comparative (`>`, "taller than"); `GreaterEqual` is the
118/// equative (`≥`, "as tall as" — at least as tall); `Equal` is the exact equative
119/// ("exactly as tall as").
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum ComparisonRelation {
122    Greater,
123    GreaterEqual,
124    Equal,
125}
126
127/// Quantifier types for first-order and generalized quantifiers.
128///
129/// Extends standard FOL with generalized quantifiers that cannot be
130/// expressed with ∀ and ∃ alone (e.g., "most", "few", "at least 3").
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum QuantifierKind {
133    /// Universal: ∀x ("every", "all", "each").
134    Universal,
135    /// Existential: ∃x ("some", "a", "an").
136    Existential,
137    /// Proportional: "most X are Y" (>50% of domain).
138    Most,
139    /// Proportional: "few X are Y" (<expected proportion).
140    Few,
141    /// Vague large quantity: "many X are Y".
142    Many,
143    /// Exact count: "exactly n X are Y".
144    Cardinal(u32),
145    /// Lower bound: "at least n X are Y".
146    AtLeast(u32),
147    /// Upper bound: "at most n X are Y".
148    AtMost(u32),
149    /// Generic: "cats meow" (characterizing generalization).
150    Generic,
151}
152
153/// Binary logical connectives.
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum BinaryOpKind {
156    /// Conjunction: P ∧ Q.
157    And,
158    /// Disjunction: P ∨ Q.
159    Or,
160    /// Material implication: P → Q.
161    Implies,
162    /// Biconditional: P ↔ Q.
163    Iff,
164}
165
166/// Unary logical operators.
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum UnaryOpKind {
169    /// Negation: ¬P.
170    Not,
171}
172
173// ═══════════════════════════════════════════════════════════════════
174// Temporal & Aspect Operators (Arthur Prior's Tense Logic)
175// ═══════════════════════════════════════════════════════════════════
176
177/// Temporal logic operators.
178///
179/// Prior-style tense operators (Past, Future) for linguistic temporality.
180/// Pnueli-style LTL operators (Always, Eventually, Next) for hardware verification.
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum TemporalOperator {
183    /// Past tense: P(φ) — "it was the case that φ".
184    Past,
185    /// Future tense: F(φ) — "it will be the case that φ".
186    Future,
187    /// Always/Globally: G(φ) — φ holds at every future state.
188    Always,
189    /// Eventually/Finally: F(φ) — φ holds at some future state.
190    Eventually,
191    /// Next: X(φ) — φ holds at the immediate next state.
192    Next,
193    /// Bounded Eventually: F≤n(φ) — φ holds within n steps.
194    /// SVA target: `##[0:n] φ`
195    BoundedEventually(u32),
196}
197
198/// Binary temporal operators (LTL).
199///
200/// These require two operands and express relationships between
201/// properties over time in hardware state machines.
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum BinaryTemporalOp {
204    /// φ U ψ — φ holds until ψ becomes true.
205    Until,
206    /// φ R ψ — dual of Until: ψ holds until φ releases it (or forever).
207    Release,
208    /// φ W ψ — weak until: φ holds until ψ, or φ holds forever.
209    WeakUntil,
210}
211
212// ═══════════════════════════════════════════════════════════════════
213// Event Semantics (Neo-Davidsonian)
214// ═══════════════════════════════════════════════════════════════════
215
216/// Neo-Davidsonian thematic roles for event semantics.
217///
218/// Following Parsons' neo-Davidsonian analysis, events are reified and
219/// participants are related to events via thematic role predicates:
220/// `∃e(Run(e) ∧ Agent(e, john) ∧ Location(e, park))`.
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub enum ThematicRole {
223    /// Animate initiator of action (e.g., "John" in "John ran").
224    Agent,
225    /// Entity affected by action (e.g., "the window" in "broke the window").
226    Patient,
227    /// Entity involved without change (e.g., "the ball" in "saw the ball").
228    Theme,
229    /// Animate entity receiving something (e.g., "Mary" in "gave Mary a book").
230    Recipient,
231    /// Destination or endpoint (e.g., "Paris" in "went to Paris").
232    Goal,
233    /// Origin or starting point (e.g., "London" in "came from London").
234    Source,
235    /// Tool or means (e.g., "a knife" in "cut with a knife").
236    Instrument,
237    /// Spatial setting (e.g., "the park" in "ran in the park").
238    Location,
239    /// Temporal setting (e.g., "yesterday" in "arrived yesterday").
240    Time,
241    /// How action was performed (e.g., "quickly" in "ran quickly").
242    Manner,
243    /// Resulting state of an argument — resultative secondary predication
244    /// (e.g., "red" in "painted the door red"): `Result(e, Red(door))`.
245    Result,
246    /// State an argument holds during the event — depictive secondary predication
247    /// (e.g., "raw" in "ate the meat raw"): `Depictive(e, Raw(meat))`.
248    Depictive,
249}
250
251/// Grammatical aspect operators for event structure.
252///
253/// Aspect describes the internal temporal structure of events,
254/// distinct from tense which locates events in time.
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum AspectOperator {
257    /// Ongoing action: "is running" → PROG(Run(e)).
258    Progressive,
259    /// Completed with present relevance: "has eaten" → PERF(Eat(e)).
260    Perfect,
261    /// Characteristic pattern: "smokes" (habitually) → HAB(Smoke(e)).
262    Habitual,
263    /// Repeated action: "kept knocking" → ITER(Knock(e)).
264    Iterative,
265}
266
267/// Grammatical voice operators.
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum VoiceOperator {
270    /// Passive voice: "was eaten" promotes patient to subject position.
271    Passive,
272}
273
274// ═══════════════════════════════════════════════════════════════════
275// Legacy Types (kept during transition)
276// ═══════════════════════════════════════════════════════════════════
277
278/// Parsed noun phrase structure for compositional interpretation.
279///
280/// Captures the internal structure of noun phrases including determiners,
281/// modifiers, and possessives for correct semantic composition.
282#[derive(Debug, Clone, Copy)]
283pub struct NounPhrase<'a> {
284    /// Definiteness: the (definite), a/an (indefinite), or bare (none).
285    pub definiteness: Option<Definiteness>,
286    /// Pre-nominal adjectives (e.g., "big red" in "big red ball").
287    pub adjectives: &'a [Symbol],
288    /// Head noun (e.g., "ball" in "big red ball").
289    pub noun: Symbol,
290    /// Possessor phrase (e.g., "John's" in "John's book").
291    pub possessor: Option<&'a NounPhrase<'a>>,
292    /// Prepositional phrase modifiers attached to noun.
293    pub pps: &'a [&'a LogicExpr<'a>],
294    /// Superlative adjective if present (e.g., "tallest").
295    pub superlative: Option<Symbol>,
296}
297
298// ═══════════════════════════════════════════════════════════════════
299// Boxed Variant Data (keeps LogicExpr enum small)
300// ═══════════════════════════════════════════════════════════════════
301
302/// Aristotelian categorical proposition data.
303///
304/// Represents the four categorical forms (A, E, I, O):
305/// - A: All S are P
306/// - E: No S are P
307/// - I: Some S are P
308/// - O: Some S are not P
309#[derive(Debug)]
310pub struct CategoricalData<'a> {
311    /// The quantifier (All, No, Some).
312    pub quantifier: TokenType,
313    /// Subject term (S in "All S are P").
314    pub subject: NounPhrase<'a>,
315    /// Whether copula is negated (for O-form: "Some S are not P").
316    pub copula_negative: bool,
317    /// Predicate term (P in "All S are P").
318    pub predicate: NounPhrase<'a>,
319}
320
321/// Simple subject-verb-object relation data.
322#[derive(Debug)]
323pub struct RelationData<'a> {
324    /// Subject noun phrase.
325    pub subject: NounPhrase<'a>,
326    /// Verb predicate.
327    pub verb: Symbol,
328    /// Object noun phrase.
329    pub object: NounPhrase<'a>,
330}
331
332/// Neo-Davidsonian event structure with thematic roles.
333///
334/// Represents a verb event with its participants decomposed into
335/// separate thematic role predicates: `∃e(Run(e) ∧ Agent(e, john))`.
336#[derive(Debug)]
337pub struct NeoEventData<'a> {
338    /// The event variable (e, e1, e2, ...).
339    pub event_var: Symbol,
340    /// The verb predicate name.
341    pub verb: Symbol,
342    /// Thematic role assignments: (Role, Filler) pairs.
343    pub roles: &'a [(ThematicRole, Term<'a>)],
344    /// Adverbial modifiers (e.g., "quickly" → Quickly(e)).
345    pub modifiers: &'a [Symbol],
346    /// When true, suppress local ∃e quantification.
347    /// Used in DRT for generic conditionals where event var is bound by outer ∀.
348    pub suppress_existential: bool,
349    /// World argument for Kripke semantics. None = implicit actual world (w₀).
350    pub world: Option<Symbol>,
351}
352
353impl<'a> NounPhrase<'a> {
354    pub fn simple(noun: Symbol) -> Self {
355        NounPhrase {
356            definiteness: None,
357            adjectives: &[],
358            noun,
359            possessor: None,
360            pps: &[],
361            superlative: None,
362        }
363    }
364
365    pub fn with_definiteness(definiteness: Definiteness, noun: Symbol) -> Self {
366        NounPhrase {
367            definiteness: Some(definiteness),
368            adjectives: &[],
369            noun,
370            possessor: None,
371            pps: &[],
372            superlative: None,
373        }
374    }
375}
376
377/// Modal logic domain classification.
378///
379/// Determines the accessibility relation in Kripke semantics:
380/// what kinds of possible worlds are relevant.
381#[derive(Debug, Clone, Copy, PartialEq)]
382pub enum ModalDomain {
383    /// Alethic modality: logical/metaphysical possibility and necessity.
384    /// "It is possible that P" = P holds in some accessible world.
385    Alethic,
386    /// Deontic modality: obligation and permission.
387    /// "It is obligatory that P" = P holds in all deontically ideal worlds.
388    Deontic,
389    /// Temporal modality: hardware state transitions.
390    /// Accessibility = next-state relation (clock-cycle transitions).
391    Temporal,
392}
393
394/// Modal flavor affecting scope interpretation.
395///
396/// The distinction between root and epistemic modals affects
397/// quantifier scope: root modals scope under quantifiers (de re),
398/// while epistemic modals scope over quantifiers (de dicto).
399#[derive(Debug, Clone, Copy, PartialEq, Eq)]
400pub enum ModalFlavor {
401    /// Root modals express ability, obligation, or circumstantial possibility.
402    /// Verbs: can, must, should, shall, could, would.
403    /// Scope: NARROW (de re) — modal attaches inside quantifier scope.
404    /// Example: "Every student can solve this" = ∀x(Student(x) → ◇Solve(x, this))
405    Root,
406    /// Epistemic modals express possibility or deduction based on evidence.
407    /// Verbs: might, may (epistemic readings).
408    /// Scope: WIDE (de dicto) — modal wraps the entire quantified formula.
409    /// Example: "A student might win" = ◇∃x(Student(x) ∧ Win(x))
410    Epistemic,
411    /// Evidential modality marks an evidence source without asserting the
412    /// complement: raising verbs seem/appear/look (§4.3).
413    /// Frame: serial, non-reflexive — Seem(⟨P⟩) does not entail P.
414    /// Example: "John seems happy" = Seem(⟨Happy(john)⟩)
415    Evidential,
416    /// Bouletic modality quantifies over preference-ideal worlds: wishes
417    /// (§1.2 optatives) and directives (§1.4 imperatives).
418    /// Frame: serial — the wished/commanded content is never entailed.
419    Bouletic,
420}
421
422/// Modal operator parameters for Kripke semantics (Kratzer-style).
423///
424/// Combines domain (what kind of modality), force (necessity vs possibility),
425/// and flavor (scope/evidence behavior) with the conversational backgrounds:
426/// a modal base f (which worlds are in play) and an ordering source g (how
427/// they are ranked). For evidentials the modal base names the evidence-source
428/// lexeme (seem/appear); counterfactuals use g = similarity.
429#[derive(Debug, Clone, Copy, PartialEq)]
430pub struct ModalVector {
431    /// The modal domain: alethic or deontic.
432    pub domain: ModalDomain,
433    /// Modal force: 1.0 = necessity (□), 0.5 = possibility (◇), graded values between.
434    pub force: f32,
435    /// Scope flavor: root (narrow scope) or epistemic (wide scope).
436    pub flavor: ModalFlavor,
437    /// Kratzer modal base f — the conversational background supplying the
438    /// accessible worlds (for evidentials: the evidence-source lexeme).
439    pub modal_base: Option<Symbol>,
440    /// Kratzer ordering source g — ranks the modal-base worlds by ideality /
441    /// normality / similarity.
442    pub ordering_source: Option<Symbol>,
443}
444
445impl ModalVector {
446    /// A modal vector with empty conversational backgrounds (f = g = None).
447    pub fn new(domain: ModalDomain, force: f32, flavor: ModalFlavor) -> Self {
448        ModalVector {
449            domain,
450            force,
451            flavor,
452            modal_base: None,
453            ordering_source: None,
454        }
455    }
456
457    /// Attach a Kratzer modal base f.
458    pub fn with_base(mut self, base: Symbol) -> Self {
459        self.modal_base = Some(base);
460        self
461    }
462}
463
464// ═══════════════════════════════════════════════════════════════════
465// Expression Enum (hybrid: old + new variants)
466// ═══════════════════════════════════════════════════════════════════
467
468/// First-order logic expression with modal, temporal, and event extensions.
469///
470/// This is the core AST type representing logical formulas. All nodes are
471/// arena-allocated with the `'a` lifetime tracking the arena's scope.
472///
473/// # Categories
474///
475/// - **Core FOL**: [`Predicate`], [`Quantifier`], [`BinaryOp`], [`UnaryOp`], [`Identity`]
476/// - **Lambda calculus**: [`Lambda`], [`App`], [`Atom`]
477/// - **Modal logic**: [`Modal`], [`Intensional`]
478/// - **Temporal/Aspect**: [`Temporal`], [`Aspectual`], [`Voice`]
479/// - **Event semantics**: [`Event`], [`NeoEvent`]
480/// - **Questions**: [`Question`], [`YesNoQuestion`]
481/// - **Pragmatics**: [`SpeechAct`], [`Focus`], [`Presupposition`]
482/// - **Comparison**: [`Comparative`], [`Superlative`]
483/// - **Other**: [`Counterfactual`], [`Causal`], [`Control`], [`Imperative`]
484///
485/// [`Predicate`]: LogicExpr::Predicate
486/// [`Quantifier`]: LogicExpr::Quantifier
487/// [`BinaryOp`]: LogicExpr::BinaryOp
488/// [`UnaryOp`]: LogicExpr::UnaryOp
489/// [`Identity`]: LogicExpr::Identity
490/// [`Lambda`]: LogicExpr::Lambda
491/// [`App`]: LogicExpr::App
492/// [`Atom`]: LogicExpr::Atom
493/// [`Modal`]: LogicExpr::Modal
494/// [`Intensional`]: LogicExpr::Intensional
495/// [`Temporal`]: LogicExpr::Temporal
496/// [`Aspectual`]: LogicExpr::Aspectual
497/// [`Voice`]: LogicExpr::Voice
498/// [`Event`]: LogicExpr::Event
499/// [`NeoEvent`]: LogicExpr::NeoEvent
500/// [`Question`]: LogicExpr::Question
501/// [`YesNoQuestion`]: LogicExpr::YesNoQuestion
502/// [`SpeechAct`]: LogicExpr::SpeechAct
503/// [`Focus`]: LogicExpr::Focus
504/// [`Presupposition`]: LogicExpr::Presupposition
505/// [`Comparative`]: LogicExpr::Comparative
506/// [`Superlative`]: LogicExpr::Superlative
507/// [`Counterfactual`]: LogicExpr::Counterfactual
508/// [`Causal`]: LogicExpr::Causal
509/// [`Control`]: LogicExpr::Control
510/// [`Imperative`]: LogicExpr::Imperative
511#[derive(Debug)]
512pub enum LogicExpr<'a> {
513    /// Atomic predicate: `P(t1, t2, ...)` with optional world parameter.
514    Predicate {
515        name: Symbol,
516        args: &'a [Term<'a>],
517        /// World argument for Kripke semantics. None = implicit actual world (w₀).
518        world: Option<Symbol>,
519    },
520
521    /// Identity statement: `t1 = t2`.
522    Identity {
523        left: &'a Term<'a>,
524        right: &'a Term<'a>,
525    },
526
527    /// Metaphorical assertion: tenor "is" vehicle (non-literal identity).
528    Metaphor {
529        tenor: &'a Term<'a>,
530        vehicle: &'a Term<'a>,
531    },
532
533    /// Quantified formula: `∀x.φ` or `∃x.φ` with scope island tracking.
534    Quantifier {
535        kind: QuantifierKind,
536        variable: Symbol,
537        body: &'a LogicExpr<'a>,
538        /// Island ID prevents illicit scope interactions across syntactic boundaries.
539        island_id: u32,
540    },
541
542    /// Aristotelian categorical proposition (boxed to keep enum small).
543    Categorical(Box<CategoricalData<'a>>),
544
545    /// Simple S-V-O relation (boxed).
546    Relation(Box<RelationData<'a>>),
547
548    /// Modal operator: □φ (necessity) or ◇φ (possibility).
549    Modal {
550        vector: ModalVector,
551        operand: &'a LogicExpr<'a>,
552    },
553
554    /// Tense/temporal operator: PAST(φ), FUTURE(φ), ALWAYS(φ), EVENTUALLY(φ), NEXT(φ).
555    Temporal {
556        operator: TemporalOperator,
557        body: &'a LogicExpr<'a>,
558    },
559
560    /// Binary temporal operator: φ UNTIL ψ, φ RELEASE ψ, φ WEAKUNTIL ψ.
561    TemporalBinary {
562        operator: BinaryTemporalOp,
563        left: &'a LogicExpr<'a>,
564        right: &'a LogicExpr<'a>,
565    },
566
567    /// Aspect operator: PROG(φ), PERF(φ), HAB(φ), ITER(φ).
568    Aspectual {
569        operator: AspectOperator,
570        body: &'a LogicExpr<'a>,
571    },
572
573    /// Voice operator: PASSIVE(φ).
574    Voice {
575        operator: VoiceOperator,
576        body: &'a LogicExpr<'a>,
577    },
578
579    /// Binary connective: φ ∧ ψ, φ ∨ ψ, φ → ψ, φ ↔ ψ.
580    BinaryOp {
581        left: &'a LogicExpr<'a>,
582        op: TokenType,
583        right: &'a LogicExpr<'a>,
584    },
585
586    /// Unary operator: ¬φ.
587    UnaryOp {
588        op: TokenType,
589        operand: &'a LogicExpr<'a>,
590    },
591
592    /// Wh-question: λx.φ where x is the questioned variable.
593    Question {
594        wh_variable: Symbol,
595        body: &'a LogicExpr<'a>,
596    },
597
598    /// Yes/no question: ?φ (is φ true?).
599    YesNoQuestion {
600        body: &'a LogicExpr<'a>,
601    },
602
603    /// Atomic symbol (variable or constant in lambda context).
604    Atom(Symbol),
605
606    /// Lambda abstraction: λx.φ.
607    Lambda {
608        variable: Symbol,
609        body: &'a LogicExpr<'a>,
610    },
611
612    /// Function application: (φ)(ψ).
613    App {
614        function: &'a LogicExpr<'a>,
615        argument: &'a LogicExpr<'a>,
616    },
617
618    /// Intensional context: `operator[content]` for opaque verbs (believes, seeks).
619    Intensional {
620        operator: Symbol,
621        content: &'a LogicExpr<'a>,
622    },
623
624    /// Legacy event semantics (Davidson-style with adverb list).
625    Event {
626        predicate: &'a LogicExpr<'a>,
627        adverbs: &'a [Symbol],
628    },
629
630    /// Neo-Davidsonian event with thematic roles (boxed).
631    NeoEvent(Box<NeoEventData<'a>>),
632
633    /// Imperative command: !φ.
634    Imperative {
635        action: &'a LogicExpr<'a>,
636    },
637
638    /// Exclamative: affective stance toward a surprisingly-high degree, with no
639    /// subject-aux inversion ("How tall she is!", "What a fool he is!"). Asserts
640    /// `∃degree_var(body ∧ degree_var ≫ θ)` and presupposes `body`.
641    Exclamative {
642        degree_var: Symbol,
643        body: &'a LogicExpr<'a>,
644    },
645
646    /// Optative: a wish with no asserted truth ("May you prosper!", "Long live the
647    /// king!", "If only it were Friday!"). → `Wish(speaker, ⟨wish⟩)`; the complement
648    /// is NOT entailed.
649    Optative {
650        wish: &'a LogicExpr<'a>,
651    },
652
653    /// Scalar implicature (§8.7): a weak scalar item asserts `assertion` and
654    /// DEFEASIBLY implicates `implicature` (the negation of a stronger Horn
655    /// alternative). "Some students passed." → ∃… +> ¬∀…. Rendered `assertion +>
656    /// implicature`; the implicature is cancellable and not part of truth conditions.
657    Implicature {
658        assertion: &'a LogicExpr<'a>,
659        implicature: &'a LogicExpr<'a>,
660    },
661
662    /// Speech act: performative utterance with illocutionary force.
663    SpeechAct {
664        performer: Symbol,
665        act_type: Symbol,
666        content: &'a LogicExpr<'a>,
667    },
668
669    /// Counterfactual conditional: "If P had been, Q would have been".
670    Counterfactual {
671        antecedent: &'a LogicExpr<'a>,
672        consequent: &'a LogicExpr<'a>,
673    },
674
675    /// Causal relation: "effect because cause".
676    Causal {
677        effect: &'a LogicExpr<'a>,
678        cause: &'a LogicExpr<'a>,
679    },
680
681    /// Concessive relation: "main, although concession" — the main clause holds
682    /// DESPITE a defeated expectation from the concession ("Although she was tired,
683    /// she finished." → Finish(she) ∧ Concessive(Tired(she))).
684    Concessive {
685        main: &'a LogicExpr<'a>,
686        concession: &'a LogicExpr<'a>,
687    },
688
689    /// Comparative / equative: "X is taller than Y" (`>`), "X is as tall as Y"
690    /// (`≥`), "X is as tall as Y and no taller" (`=`). The `relation` selects the
691    /// degree comparison; `difference` is the optional measure ("by 2 inches").
692    Comparative {
693        adjective: Symbol,
694        subject: &'a Term<'a>,
695        object: &'a Term<'a>,
696        difference: Option<&'a Term<'a>>,
697        relation: ComparisonRelation,
698    },
699
700    /// Superlative: "X is the tallest among domain".
701    Superlative {
702        adjective: Symbol,
703        subject: &'a Term<'a>,
704        domain: Symbol,
705    },
706
707    /// Scopal adverb: "only", "always", etc. as operators.
708    Scopal {
709        operator: Symbol,
710        body: &'a LogicExpr<'a>,
711    },
712
713    /// Control verb: "wants to VP", "persuaded X to VP".
714    Control {
715        verb: Symbol,
716        subject: &'a Term<'a>,
717        object: Option<&'a Term<'a>>,
718        infinitive: &'a LogicExpr<'a>,
719    },
720
721    /// Presupposition-assertion structure.
722    Presupposition {
723        assertion: &'a LogicExpr<'a>,
724        presupposition: &'a LogicExpr<'a>,
725    },
726
727    /// Focus particle: "only X", "even X" with alternative set.
728    Focus {
729        kind: crate::token::FocusKind,
730        focused: &'a Term<'a>,
731        scope: &'a LogicExpr<'a>,
732    },
733
734    /// Temporal anchor: "yesterday(φ)", "now(φ)".
735    TemporalAnchor {
736        anchor: Symbol,
737        body: &'a LogicExpr<'a>,
738    },
739
740    /// Distributive operator: *P distributes P over group members.
741    Distributive {
742        predicate: &'a LogicExpr<'a>,
743    },
744
745    /// Group quantifier for collective cardinal readings.
746    /// `∃g(Group(g) ∧ Count(g,n) ∧ ∀x(Member(x,g) → Restriction(x)) ∧ Body(g))`
747    GroupQuantifier {
748        group_var: Symbol,
749        count: u32,
750        member_var: Symbol,
751        restriction: &'a LogicExpr<'a>,
752        body: &'a LogicExpr<'a>,
753    },
754}
755
756impl<'a> LogicExpr<'a> {
757    pub fn lambda(var: Symbol, body: &'a LogicExpr<'a>, arena: &'a Arena<LogicExpr<'a>>) -> &'a LogicExpr<'a> {
758        arena.alloc(LogicExpr::Lambda {
759            variable: var,
760            body,
761        })
762    }
763
764    pub fn app(func: &'a LogicExpr<'a>, arg: &'a LogicExpr<'a>, arena: &'a Arena<LogicExpr<'a>>) -> &'a LogicExpr<'a> {
765        arena.alloc(LogicExpr::App {
766            function: func,
767            argument: arg,
768        })
769    }
770}
771
772#[cfg(test)]
773mod size_tests {
774    use super::*;
775    use std::mem::size_of;
776
777    #[test]
778    fn test_ast_node_sizes() {
779        println!("LogicExpr size: {} bytes", size_of::<LogicExpr>());
780        println!("Term size: {} bytes", size_of::<Term>());
781        println!("NounPhrase size: {} bytes", size_of::<NounPhrase>());
782
783        assert!(
784            size_of::<LogicExpr>() <= 48,
785            "LogicExpr is {} bytes - consider boxing large variants",
786            size_of::<LogicExpr>()
787        );
788        assert!(
789            size_of::<Term>() <= 32,
790            "Term is {} bytes",
791            size_of::<Term>()
792        );
793    }
794}