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 /// Sentential complement (embedded clause as propositional argument).
101 Proposition(&'a LogicExpr<'a>),
102 /// Numeric value with optional unit and dimension.
103 Value {
104 kind: NumberKind,
105 unit: Option<Symbol>,
106 dimension: Option<Dimension>,
107 },
108}
109
110/// Quantifier types for first-order and generalized quantifiers.
111///
112/// Extends standard FOL with generalized quantifiers that cannot be
113/// expressed with ∀ and ∃ alone (e.g., "most", "few", "at least 3").
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum QuantifierKind {
116 /// Universal: ∀x ("every", "all", "each").
117 Universal,
118 /// Existential: ∃x ("some", "a", "an").
119 Existential,
120 /// Proportional: "most X are Y" (>50% of domain).
121 Most,
122 /// Proportional: "few X are Y" (<expected proportion).
123 Few,
124 /// Vague large quantity: "many X are Y".
125 Many,
126 /// Exact count: "exactly n X are Y".
127 Cardinal(u32),
128 /// Lower bound: "at least n X are Y".
129 AtLeast(u32),
130 /// Upper bound: "at most n X are Y".
131 AtMost(u32),
132 /// Generic: "cats meow" (characterizing generalization).
133 Generic,
134}
135
136/// Binary logical connectives.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum BinaryOpKind {
139 /// Conjunction: P ∧ Q.
140 And,
141 /// Disjunction: P ∨ Q.
142 Or,
143 /// Material implication: P → Q.
144 Implies,
145 /// Biconditional: P ↔ Q.
146 Iff,
147}
148
149/// Unary logical operators.
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum UnaryOpKind {
152 /// Negation: ¬P.
153 Not,
154}
155
156// ═══════════════════════════════════════════════════════════════════
157// Temporal & Aspect Operators (Arthur Prior's Tense Logic)
158// ═══════════════════════════════════════════════════════════════════
159
160/// Temporal logic operators.
161///
162/// Prior-style tense operators (Past, Future) for linguistic temporality.
163/// Pnueli-style LTL operators (Always, Eventually, Next) for hardware verification.
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum TemporalOperator {
166 /// Past tense: P(φ) — "it was the case that φ".
167 Past,
168 /// Future tense: F(φ) — "it will be the case that φ".
169 Future,
170 /// Always/Globally: G(φ) — φ holds at every future state.
171 Always,
172 /// Eventually/Finally: F(φ) — φ holds at some future state.
173 Eventually,
174 /// Next: X(φ) — φ holds at the immediate next state.
175 Next,
176}
177
178/// Binary temporal operators (LTL).
179///
180/// These require two operands and express relationships between
181/// properties over time in hardware state machines.
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum BinaryTemporalOp {
184 /// φ U ψ — φ holds until ψ becomes true.
185 Until,
186 /// φ R ψ — dual of Until: ψ holds until φ releases it (or forever).
187 Release,
188 /// φ W ψ — weak until: φ holds until ψ, or φ holds forever.
189 WeakUntil,
190}
191
192// ═══════════════════════════════════════════════════════════════════
193// Event Semantics (Neo-Davidsonian)
194// ═══════════════════════════════════════════════════════════════════
195
196/// Neo-Davidsonian thematic roles for event semantics.
197///
198/// Following Parsons' neo-Davidsonian analysis, events are reified and
199/// participants are related to events via thematic role predicates:
200/// `∃e(Run(e) ∧ Agent(e, john) ∧ Location(e, park))`.
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum ThematicRole {
203 /// Animate initiator of action (e.g., "John" in "John ran").
204 Agent,
205 /// Entity affected by action (e.g., "the window" in "broke the window").
206 Patient,
207 /// Entity involved without change (e.g., "the ball" in "saw the ball").
208 Theme,
209 /// Animate entity receiving something (e.g., "Mary" in "gave Mary a book").
210 Recipient,
211 /// Destination or endpoint (e.g., "Paris" in "went to Paris").
212 Goal,
213 /// Origin or starting point (e.g., "London" in "came from London").
214 Source,
215 /// Tool or means (e.g., "a knife" in "cut with a knife").
216 Instrument,
217 /// Spatial setting (e.g., "the park" in "ran in the park").
218 Location,
219 /// Temporal setting (e.g., "yesterday" in "arrived yesterday").
220 Time,
221 /// How action was performed (e.g., "quickly" in "ran quickly").
222 Manner,
223}
224
225/// Grammatical aspect operators for event structure.
226///
227/// Aspect describes the internal temporal structure of events,
228/// distinct from tense which locates events in time.
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230pub enum AspectOperator {
231 /// Ongoing action: "is running" → PROG(Run(e)).
232 Progressive,
233 /// Completed with present relevance: "has eaten" → PERF(Eat(e)).
234 Perfect,
235 /// Characteristic pattern: "smokes" (habitually) → HAB(Smoke(e)).
236 Habitual,
237 /// Repeated action: "kept knocking" → ITER(Knock(e)).
238 Iterative,
239}
240
241/// Grammatical voice operators.
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub enum VoiceOperator {
244 /// Passive voice: "was eaten" promotes patient to subject position.
245 Passive,
246}
247
248// ═══════════════════════════════════════════════════════════════════
249// Legacy Types (kept during transition)
250// ═══════════════════════════════════════════════════════════════════
251
252/// Parsed noun phrase structure for compositional interpretation.
253///
254/// Captures the internal structure of noun phrases including determiners,
255/// modifiers, and possessives for correct semantic composition.
256#[derive(Debug)]
257pub struct NounPhrase<'a> {
258 /// Definiteness: the (definite), a/an (indefinite), or bare (none).
259 pub definiteness: Option<Definiteness>,
260 /// Pre-nominal adjectives (e.g., "big red" in "big red ball").
261 pub adjectives: &'a [Symbol],
262 /// Head noun (e.g., "ball" in "big red ball").
263 pub noun: Symbol,
264 /// Possessor phrase (e.g., "John's" in "John's book").
265 pub possessor: Option<&'a NounPhrase<'a>>,
266 /// Prepositional phrase modifiers attached to noun.
267 pub pps: &'a [&'a LogicExpr<'a>],
268 /// Superlative adjective if present (e.g., "tallest").
269 pub superlative: Option<Symbol>,
270}
271
272// ═══════════════════════════════════════════════════════════════════
273// Boxed Variant Data (keeps LogicExpr enum small)
274// ═══════════════════════════════════════════════════════════════════
275
276/// Aristotelian categorical proposition data.
277///
278/// Represents the four categorical forms (A, E, I, O):
279/// - A: All S are P
280/// - E: No S are P
281/// - I: Some S are P
282/// - O: Some S are not P
283#[derive(Debug)]
284pub struct CategoricalData<'a> {
285 /// The quantifier (All, No, Some).
286 pub quantifier: TokenType,
287 /// Subject term (S in "All S are P").
288 pub subject: NounPhrase<'a>,
289 /// Whether copula is negated (for O-form: "Some S are not P").
290 pub copula_negative: bool,
291 /// Predicate term (P in "All S are P").
292 pub predicate: NounPhrase<'a>,
293}
294
295/// Simple subject-verb-object relation data.
296#[derive(Debug)]
297pub struct RelationData<'a> {
298 /// Subject noun phrase.
299 pub subject: NounPhrase<'a>,
300 /// Verb predicate.
301 pub verb: Symbol,
302 /// Object noun phrase.
303 pub object: NounPhrase<'a>,
304}
305
306/// Neo-Davidsonian event structure with thematic roles.
307///
308/// Represents a verb event with its participants decomposed into
309/// separate thematic role predicates: `∃e(Run(e) ∧ Agent(e, john))`.
310#[derive(Debug)]
311pub struct NeoEventData<'a> {
312 /// The event variable (e, e1, e2, ...).
313 pub event_var: Symbol,
314 /// The verb predicate name.
315 pub verb: Symbol,
316 /// Thematic role assignments: (Role, Filler) pairs.
317 pub roles: &'a [(ThematicRole, Term<'a>)],
318 /// Adverbial modifiers (e.g., "quickly" → Quickly(e)).
319 pub modifiers: &'a [Symbol],
320 /// When true, suppress local ∃e quantification.
321 /// Used in DRT for generic conditionals where event var is bound by outer ∀.
322 pub suppress_existential: bool,
323 /// World argument for Kripke semantics. None = implicit actual world (w₀).
324 pub world: Option<Symbol>,
325}
326
327impl<'a> NounPhrase<'a> {
328 pub fn simple(noun: Symbol) -> Self {
329 NounPhrase {
330 definiteness: None,
331 adjectives: &[],
332 noun,
333 possessor: None,
334 pps: &[],
335 superlative: None,
336 }
337 }
338
339 pub fn with_definiteness(definiteness: Definiteness, noun: Symbol) -> Self {
340 NounPhrase {
341 definiteness: Some(definiteness),
342 adjectives: &[],
343 noun,
344 possessor: None,
345 pps: &[],
346 superlative: None,
347 }
348 }
349}
350
351/// Modal logic domain classification.
352///
353/// Determines the accessibility relation in Kripke semantics:
354/// what kinds of possible worlds are relevant.
355#[derive(Debug, Clone, Copy, PartialEq)]
356pub enum ModalDomain {
357 /// Alethic modality: logical/metaphysical possibility and necessity.
358 /// "It is possible that P" = P holds in some accessible world.
359 Alethic,
360 /// Deontic modality: obligation and permission.
361 /// "It is obligatory that P" = P holds in all deontically ideal worlds.
362 Deontic,
363 /// Temporal modality: hardware state transitions.
364 /// Accessibility = next-state relation (clock-cycle transitions).
365 Temporal,
366}
367
368/// Modal flavor affecting scope interpretation.
369///
370/// The distinction between root and epistemic modals affects
371/// quantifier scope: root modals scope under quantifiers (de re),
372/// while epistemic modals scope over quantifiers (de dicto).
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
374pub enum ModalFlavor {
375 /// Root modals express ability, obligation, or circumstantial possibility.
376 /// Verbs: can, must, should, shall, could, would.
377 /// Scope: NARROW (de re) — modal attaches inside quantifier scope.
378 /// Example: "Every student can solve this" = ∀x(Student(x) → ◇Solve(x, this))
379 Root,
380 /// Epistemic modals express possibility or deduction based on evidence.
381 /// Verbs: might, may (epistemic readings).
382 /// Scope: WIDE (de dicto) — modal wraps the entire quantified formula.
383 /// Example: "A student might win" = ◇∃x(Student(x) ∧ Win(x))
384 Epistemic,
385}
386
387/// Modal operator parameters for Kripke semantics.
388///
389/// Combines domain (what kind of modality), force (necessity vs possibility),
390/// and flavor (scope behavior) into a single modal specification.
391#[derive(Debug, Clone, Copy, PartialEq)]
392pub struct ModalVector {
393 /// The modal domain: alethic or deontic.
394 pub domain: ModalDomain,
395 /// Modal force: 1.0 = necessity (□), 0.5 = possibility (◇), graded values between.
396 pub force: f32,
397 /// Scope flavor: root (narrow scope) or epistemic (wide scope).
398 pub flavor: ModalFlavor,
399}
400
401// ═══════════════════════════════════════════════════════════════════
402// Expression Enum (hybrid: old + new variants)
403// ═══════════════════════════════════════════════════════════════════
404
405/// First-order logic expression with modal, temporal, and event extensions.
406///
407/// This is the core AST type representing logical formulas. All nodes are
408/// arena-allocated with the `'a` lifetime tracking the arena's scope.
409///
410/// # Categories
411///
412/// - **Core FOL**: [`Predicate`], [`Quantifier`], [`BinaryOp`], [`UnaryOp`], [`Identity`]
413/// - **Lambda calculus**: [`Lambda`], [`App`], [`Atom`]
414/// - **Modal logic**: [`Modal`], [`Intensional`]
415/// - **Temporal/Aspect**: [`Temporal`], [`Aspectual`], [`Voice`]
416/// - **Event semantics**: [`Event`], [`NeoEvent`]
417/// - **Questions**: [`Question`], [`YesNoQuestion`]
418/// - **Pragmatics**: [`SpeechAct`], [`Focus`], [`Presupposition`]
419/// - **Comparison**: [`Comparative`], [`Superlative`]
420/// - **Other**: [`Counterfactual`], [`Causal`], [`Control`], [`Imperative`]
421///
422/// [`Predicate`]: LogicExpr::Predicate
423/// [`Quantifier`]: LogicExpr::Quantifier
424/// [`BinaryOp`]: LogicExpr::BinaryOp
425/// [`UnaryOp`]: LogicExpr::UnaryOp
426/// [`Identity`]: LogicExpr::Identity
427/// [`Lambda`]: LogicExpr::Lambda
428/// [`App`]: LogicExpr::App
429/// [`Atom`]: LogicExpr::Atom
430/// [`Modal`]: LogicExpr::Modal
431/// [`Intensional`]: LogicExpr::Intensional
432/// [`Temporal`]: LogicExpr::Temporal
433/// [`Aspectual`]: LogicExpr::Aspectual
434/// [`Voice`]: LogicExpr::Voice
435/// [`Event`]: LogicExpr::Event
436/// [`NeoEvent`]: LogicExpr::NeoEvent
437/// [`Question`]: LogicExpr::Question
438/// [`YesNoQuestion`]: LogicExpr::YesNoQuestion
439/// [`SpeechAct`]: LogicExpr::SpeechAct
440/// [`Focus`]: LogicExpr::Focus
441/// [`Presupposition`]: LogicExpr::Presupposition
442/// [`Comparative`]: LogicExpr::Comparative
443/// [`Superlative`]: LogicExpr::Superlative
444/// [`Counterfactual`]: LogicExpr::Counterfactual
445/// [`Causal`]: LogicExpr::Causal
446/// [`Control`]: LogicExpr::Control
447/// [`Imperative`]: LogicExpr::Imperative
448#[derive(Debug)]
449pub enum LogicExpr<'a> {
450 /// Atomic predicate: `P(t1, t2, ...)` with optional world parameter.
451 Predicate {
452 name: Symbol,
453 args: &'a [Term<'a>],
454 /// World argument for Kripke semantics. None = implicit actual world (w₀).
455 world: Option<Symbol>,
456 },
457
458 /// Identity statement: `t1 = t2`.
459 Identity {
460 left: &'a Term<'a>,
461 right: &'a Term<'a>,
462 },
463
464 /// Metaphorical assertion: tenor "is" vehicle (non-literal identity).
465 Metaphor {
466 tenor: &'a Term<'a>,
467 vehicle: &'a Term<'a>,
468 },
469
470 /// Quantified formula: `∀x.φ` or `∃x.φ` with scope island tracking.
471 Quantifier {
472 kind: QuantifierKind,
473 variable: Symbol,
474 body: &'a LogicExpr<'a>,
475 /// Island ID prevents illicit scope interactions across syntactic boundaries.
476 island_id: u32,
477 },
478
479 /// Aristotelian categorical proposition (boxed to keep enum small).
480 Categorical(Box<CategoricalData<'a>>),
481
482 /// Simple S-V-O relation (boxed).
483 Relation(Box<RelationData<'a>>),
484
485 /// Modal operator: □φ (necessity) or ◇φ (possibility).
486 Modal {
487 vector: ModalVector,
488 operand: &'a LogicExpr<'a>,
489 },
490
491 /// Tense/temporal operator: PAST(φ), FUTURE(φ), ALWAYS(φ), EVENTUALLY(φ), NEXT(φ).
492 Temporal {
493 operator: TemporalOperator,
494 body: &'a LogicExpr<'a>,
495 },
496
497 /// Binary temporal operator: φ UNTIL ψ, φ RELEASE ψ, φ WEAKUNTIL ψ.
498 TemporalBinary {
499 operator: BinaryTemporalOp,
500 left: &'a LogicExpr<'a>,
501 right: &'a LogicExpr<'a>,
502 },
503
504 /// Aspect operator: PROG(φ), PERF(φ), HAB(φ), ITER(φ).
505 Aspectual {
506 operator: AspectOperator,
507 body: &'a LogicExpr<'a>,
508 },
509
510 /// Voice operator: PASSIVE(φ).
511 Voice {
512 operator: VoiceOperator,
513 body: &'a LogicExpr<'a>,
514 },
515
516 /// Binary connective: φ ∧ ψ, φ ∨ ψ, φ → ψ, φ ↔ ψ.
517 BinaryOp {
518 left: &'a LogicExpr<'a>,
519 op: TokenType,
520 right: &'a LogicExpr<'a>,
521 },
522
523 /// Unary operator: ¬φ.
524 UnaryOp {
525 op: TokenType,
526 operand: &'a LogicExpr<'a>,
527 },
528
529 /// Wh-question: λx.φ where x is the questioned variable.
530 Question {
531 wh_variable: Symbol,
532 body: &'a LogicExpr<'a>,
533 },
534
535 /// Yes/no question: ?φ (is φ true?).
536 YesNoQuestion {
537 body: &'a LogicExpr<'a>,
538 },
539
540 /// Atomic symbol (variable or constant in lambda context).
541 Atom(Symbol),
542
543 /// Lambda abstraction: λx.φ.
544 Lambda {
545 variable: Symbol,
546 body: &'a LogicExpr<'a>,
547 },
548
549 /// Function application: (φ)(ψ).
550 App {
551 function: &'a LogicExpr<'a>,
552 argument: &'a LogicExpr<'a>,
553 },
554
555 /// Intensional context: `operator[content]` for opaque verbs (believes, seeks).
556 Intensional {
557 operator: Symbol,
558 content: &'a LogicExpr<'a>,
559 },
560
561 /// Legacy event semantics (Davidson-style with adverb list).
562 Event {
563 predicate: &'a LogicExpr<'a>,
564 adverbs: &'a [Symbol],
565 },
566
567 /// Neo-Davidsonian event with thematic roles (boxed).
568 NeoEvent(Box<NeoEventData<'a>>),
569
570 /// Imperative command: !φ.
571 Imperative {
572 action: &'a LogicExpr<'a>,
573 },
574
575 /// Speech act: performative utterance with illocutionary force.
576 SpeechAct {
577 performer: Symbol,
578 act_type: Symbol,
579 content: &'a LogicExpr<'a>,
580 },
581
582 /// Counterfactual conditional: "If P had been, Q would have been".
583 Counterfactual {
584 antecedent: &'a LogicExpr<'a>,
585 consequent: &'a LogicExpr<'a>,
586 },
587
588 /// Causal relation: "effect because cause".
589 Causal {
590 effect: &'a LogicExpr<'a>,
591 cause: &'a LogicExpr<'a>,
592 },
593
594 /// Comparative: "X is taller than Y (by 2 inches)".
595 Comparative {
596 adjective: Symbol,
597 subject: &'a Term<'a>,
598 object: &'a Term<'a>,
599 difference: Option<&'a Term<'a>>,
600 },
601
602 /// Superlative: "X is the tallest among domain".
603 Superlative {
604 adjective: Symbol,
605 subject: &'a Term<'a>,
606 domain: Symbol,
607 },
608
609 /// Scopal adverb: "only", "always", etc. as operators.
610 Scopal {
611 operator: Symbol,
612 body: &'a LogicExpr<'a>,
613 },
614
615 /// Control verb: "wants to VP", "persuaded X to VP".
616 Control {
617 verb: Symbol,
618 subject: &'a Term<'a>,
619 object: Option<&'a Term<'a>>,
620 infinitive: &'a LogicExpr<'a>,
621 },
622
623 /// Presupposition-assertion structure.
624 Presupposition {
625 assertion: &'a LogicExpr<'a>,
626 presupposition: &'a LogicExpr<'a>,
627 },
628
629 /// Focus particle: "only X", "even X" with alternative set.
630 Focus {
631 kind: crate::token::FocusKind,
632 focused: &'a Term<'a>,
633 scope: &'a LogicExpr<'a>,
634 },
635
636 /// Temporal anchor: "yesterday(φ)", "now(φ)".
637 TemporalAnchor {
638 anchor: Symbol,
639 body: &'a LogicExpr<'a>,
640 },
641
642 /// Distributive operator: *P distributes P over group members.
643 Distributive {
644 predicate: &'a LogicExpr<'a>,
645 },
646
647 /// Group quantifier for collective cardinal readings.
648 /// `∃g(Group(g) ∧ Count(g,n) ∧ ∀x(Member(x,g) → Restriction(x)) ∧ Body(g))`
649 GroupQuantifier {
650 group_var: Symbol,
651 count: u32,
652 member_var: Symbol,
653 restriction: &'a LogicExpr<'a>,
654 body: &'a LogicExpr<'a>,
655 },
656}
657
658impl<'a> LogicExpr<'a> {
659 pub fn lambda(var: Symbol, body: &'a LogicExpr<'a>, arena: &'a Arena<LogicExpr<'a>>) -> &'a LogicExpr<'a> {
660 arena.alloc(LogicExpr::Lambda {
661 variable: var,
662 body,
663 })
664 }
665
666 pub fn app(func: &'a LogicExpr<'a>, arg: &'a LogicExpr<'a>, arena: &'a Arena<LogicExpr<'a>>) -> &'a LogicExpr<'a> {
667 arena.alloc(LogicExpr::App {
668 function: func,
669 argument: arg,
670 })
671 }
672}
673
674#[cfg(test)]
675mod size_tests {
676 use super::*;
677 use std::mem::size_of;
678
679 #[test]
680 fn test_ast_node_sizes() {
681 println!("LogicExpr size: {} bytes", size_of::<LogicExpr>());
682 println!("Term size: {} bytes", size_of::<Term>());
683 println!("NounPhrase size: {} bytes", size_of::<NounPhrase>());
684
685 assert!(
686 size_of::<LogicExpr>() <= 48,
687 "LogicExpr is {} bytes - consider boxing large variants",
688 size_of::<LogicExpr>()
689 );
690 assert!(
691 size_of::<Term>() <= 32,
692 "Term is {} bytes",
693 size_of::<Term>()
694 );
695 }
696}