logicaffeine-language 0.9.13

Natural language to first-order logic pipeline
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
//! Imperative statement AST types for the LOGOS language.
//!
//! This module defines statement types for the imperative fragment including:
//!
//! - **[`Stmt`]**: Statement variants (let, if, match, while, for, function defs)
//! - **[`Expr`]**: Imperative expressions (field access, method calls, literals)
//! - **[`TypeExpr`]**: Type annotations with refinements and generics
//! - **[`Literal`]**: Literal values (numbers, strings, booleans)
//! - **[`Block`]**: Statement blocks with optional return expressions
//!
//! The imperative AST is used in LOGOS mode for generating executable Rust code.

use std::collections::HashSet;

use super::logic::LogicExpr;
use super::theorem::TheoremBlock;
use logicaffeine_base::Symbol;

/// Per-function optimization control flags.
///
/// Annotations placed above `## To` disable specific optimization passes
/// for that function. This gives the programmer explicit control when
/// an optimization hurts rather than helps (e.g., memoization on a function
/// whose body is cheaper than a hash lookup).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OptFlag {
    /// `## No Memo` — disable auto-memoization (TLS FxHashMap cache)
    NoMemo,
    /// `## No TCO` — disable tail-call elimination (loop conversion)
    NoTCO,
    /// `## No Peephole` — disable peephole patterns (swap, vec-fill, for-range, etc.)
    NoPeephole,
    /// `## No Borrow` — disable readonly/mutable borrow analysis (&[T]/&mut [T] params)
    NoBorrow,
    /// `## No Optimize` — disable ALL of the above (master switch)
    NoOptimize,
}

/// Type expression for explicit type annotations.
///
/// Represents type syntax like:
/// - `Int` → Primitive(Int)
/// - `User` → Named(User)
/// - `List of Int` → Generic { base: List, params: [Primitive(Int)] }
/// - `List of List of Int` → Generic { base: List, params: [Generic { base: List, params: [Primitive(Int)] }] }
/// - `Result of Int and Text` → Generic { base: Result, params: [Primitive(Int), Primitive(Text)] }
#[derive(Debug, Clone)]
pub enum TypeExpr<'a> {
    /// Primitive type: Int, Nat, Text, Bool
    Primitive(Symbol),
    /// Named type (user-defined): User, Point
    Named(Symbol),
    /// Generic type: List of Int, Option of Text, Result of Int and Text
    Generic {
        base: Symbol,
        params: &'a [TypeExpr<'a>],
    },
    /// Function type: fn(A, B) -> C (for higher-order functions)
    Function {
        inputs: &'a [TypeExpr<'a>],
        output: &'a TypeExpr<'a>,
    },
    /// Refinement type with predicate constraint.
    /// Example: `Int where it > 0`
    Refinement {
        /// The base type being refined
        base: &'a TypeExpr<'a>,
        /// The bound variable (usually "it")
        var: Symbol,
        /// The predicate constraint (from Logic Kernel)
        predicate: &'a LogicExpr<'a>,
    },
    /// Persistent storage wrapper type.
    /// Example: `Persistent Counter`
    /// Semantics: Wraps a Shared type with journal-backed storage
    Persistent {
        /// The inner type (must be a Shared/CRDT type)
        inner: &'a TypeExpr<'a>,
    },
}

/// Source for Read statements.
#[derive(Debug, Clone, Copy)]
pub enum ReadSource<'a> {
    /// Read from console (stdin)
    Console,
    /// Read from file at given path
    File(&'a Expr<'a>),
}

/// Pattern for loop variable binding.
/// Supports single identifiers and tuple destructuring for Map iteration.
#[derive(Debug, Clone)]
pub enum Pattern {
    /// Single identifier: `Repeat for x in list`
    Identifier(Symbol),
    /// Tuple destructuring: `Repeat for (k, v) in map`
    Tuple(Vec<Symbol>),
}

/// Binary operation kinds for imperative expressions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinaryOpKind {
    Add,
    Subtract,
    Multiply,
    Divide,
    Modulo,
    Eq,
    NotEq,
    Lt,
    Gt,
    LtEq,
    GtEq,
    // Logical/bitwise operators — type-aware in codegen (&&/|| for Bool, &/| for Int)
    And,
    Or,
    /// String concatenation ("X combined with Y")
    Concat,
    /// Bitwise XOR: "x xor y" → `x ^ y`
    BitXor,
    /// Left shift: "x shifted left by y" → `x << y`
    Shl,
    /// Right shift: "x shifted right by y" → `x >> y`
    Shr,
}

/// Block is a sequence of statements.
pub type Block<'a> = &'a [Stmt<'a>];

/// Match arm for pattern matching in Inspect statements.
#[derive(Debug, Clone)]
pub struct MatchArm<'a> {
    pub enum_name: Option<Symbol>,          // The enum type (e.g., Shape)
    pub variant: Option<Symbol>,            // None = Otherwise (wildcard)
    pub bindings: Vec<(Symbol, Symbol)>,    // (field_name, binding_name)
    pub body: Block<'a>,
}

/// Imperative statement AST (LOGOS §15.0.0).
///
/// Stmt is the primary AST node for imperative code blocks like `## Main`
/// and function bodies. The Assert variant bridges to the Logic Kernel.
#[derive(Debug, Clone)]
pub enum Stmt<'a> {
    /// Variable binding: `Let x be 5.` or `Let x: Int be 5.`
    Let {
        var: Symbol,
        ty: Option<&'a TypeExpr<'a>>,
        value: &'a Expr<'a>,
        mutable: bool,
    },

    /// Mutation: `Set x to 10.`
    Set {
        target: Symbol,
        value: &'a Expr<'a>,
    },

    /// Function call as statement: `Call process with data.`
    Call {
        function: Symbol,
        args: Vec<&'a Expr<'a>>,
    },

    /// Conditional: `If condition: ... Otherwise: ...`
    If {
        cond: &'a Expr<'a>,
        then_block: Block<'a>,
        else_block: Option<Block<'a>>,
    },

    /// Loop: `While condition: ...` or `While condition (decreasing expr): ...`
    While {
        cond: &'a Expr<'a>,
        body: Block<'a>,
        /// Optional decreasing variant for termination proof.
        decreasing: Option<&'a Expr<'a>>,
    },

    /// Iteration: `Repeat for x in list: ...` or `Repeat for i from 1 to 10: ...`
    Repeat {
        pattern: Pattern,  // Changed from `var: Symbol` to support tuple destructuring
        iterable: &'a Expr<'a>,
        body: Block<'a>,
    },

    /// Return: `Return x.` or `Return.`
    Return {
        value: Option<&'a Expr<'a>>,
    },

    /// Break: `Break.` — exits the innermost while loop.
    Break,

    /// Bridge to Logic Kernel: `Assert that P.`
    Assert {
        proposition: &'a LogicExpr<'a>,
    },

    /// Documented assertion with justification.
    /// `Trust that P because "reason".`
    /// Semantics: Documented runtime check that could be verified statically.
    Trust {
        proposition: &'a LogicExpr<'a>,
        justification: Symbol,
    },

    /// Runtime assertion with imperative condition
    /// `Assert that condition.` (for imperative mode)
    RuntimeAssert {
        condition: &'a Expr<'a>,
    },

    /// Ownership transfer (move): `Give x to processor.`
    /// Semantics: Move ownership of `object` to `recipient`.
    Give {
        object: &'a Expr<'a>,
        recipient: &'a Expr<'a>,
    },

    /// Immutable borrow: `Show x to console.`
    /// Semantics: Immutable borrow of `object` passed to `recipient`.
    Show {
        object: &'a Expr<'a>,
        recipient: &'a Expr<'a>,
    },

    /// Field mutation: `Set p's x to 10.`
    SetField {
        object: &'a Expr<'a>,
        field: Symbol,
        value: &'a Expr<'a>,
    },

    /// Struct definition for codegen.
    StructDef {
        name: Symbol,
        fields: Vec<(Symbol, Symbol, bool)>, // (name, type_name, is_public)
        is_portable: bool,                    // Derives Serialize/Deserialize
    },

    /// Function definition.
    FunctionDef {
        name: Symbol,
        /// Generic type parameters: empty for monomorphic functions, e.g. `[T, U]` for polymorphic.
        generics: Vec<Symbol>,
        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
        body: Block<'a>,
        return_type: Option<&'a TypeExpr<'a>>,
        is_native: bool,
        /// Rust path for user-defined native functions (e.g., "reqwest::blocking::get").
        /// None for system native functions (read, write, etc.) which use map_native_function().
        native_path: Option<Symbol>,
        /// Whether this function is exported for FFI (C ABI or WASM).
        is_exported: bool,
        /// Export target: None = C ABI (#[no_mangle] extern "C"), Some("wasm") = #[wasm_bindgen].
        export_target: Option<Symbol>,
        /// Per-function optimization flags from `## No <X>` annotations.
        opt_flags: HashSet<OptFlag>,
    },

    /// Pattern matching on sum types.
    Inspect {
        target: &'a Expr<'a>,
        arms: Vec<MatchArm<'a>>,
        has_otherwise: bool,            // For exhaustiveness tracking
    },

    /// Push to collection: `Push x to items.`
    Push {
        value: &'a Expr<'a>,
        collection: &'a Expr<'a>,
    },

    /// Pop from collection: `Pop from items.` or `Pop from items into y.`
    Pop {
        collection: &'a Expr<'a>,
        into: Option<Symbol>,
    },

    /// Add to set: `Add x to set.`
    Add {
        value: &'a Expr<'a>,
        collection: &'a Expr<'a>,
    },

    /// Remove from set: `Remove x from set.`
    Remove {
        value: &'a Expr<'a>,
        collection: &'a Expr<'a>,
    },

    /// Index assignment: `Set item N of X to Y.`
    SetIndex {
        collection: &'a Expr<'a>,
        index: &'a Expr<'a>,
        value: &'a Expr<'a>,
    },

    /// Memory arena block (Zone).
    /// "Inside a new zone called 'Scratch':"
    /// "Inside a zone called 'Buffer' of size 1 MB:"
    /// "Inside a zone called 'Data' mapped from 'file.bin':"
    Zone {
        /// The variable name for the arena handle (e.g., "Scratch")
        name: Symbol,
        /// Optional pre-allocated capacity in bytes (Heap zones only)
        capacity: Option<usize>,
        /// Optional file path for memory-mapped zones (Mapped zones only)
        source_file: Option<Symbol>,
        /// The code block executed within this memory context
        body: Block<'a>,
    },

    /// Concurrent execution block (async, I/O-bound).
    /// "Attempt all of the following:"
    /// Semantics: All tasks run concurrently via tokio::join!
    /// Best for: network requests, file I/O, waiting operations
    Concurrent {
        /// The statements to execute concurrently
        tasks: Block<'a>,
    },

    /// Parallel execution block (CPU-bound).
    /// "Simultaneously:"
    /// Semantics: True parallelism via rayon::join or thread::spawn
    /// Best for: computation, data processing, number crunching
    Parallel {
        /// The statements to execute in parallel
        tasks: Block<'a>,
    },

    /// Read from console or file.
    /// `Read input from the console.` or `Read data from file "path.txt".`
    ReadFrom {
        var: Symbol,
        source: ReadSource<'a>,
    },

    /// Write to file.
    /// `Write "content" to file "output.txt".`
    WriteFile {
        content: &'a Expr<'a>,
        path: &'a Expr<'a>,
    },

    /// Spawn an agent.
    /// `Spawn a Worker called "w1".`
    Spawn {
        agent_type: Symbol,
        name: Symbol,
    },

    /// Send message to agent.
    /// `Send Ping to "agent".`
    SendMessage {
        message: &'a Expr<'a>,
        destination: &'a Expr<'a>,
    },

    /// Await response from agent.
    /// `Await response from "agent" into result.`
    AwaitMessage {
        source: &'a Expr<'a>,
        into: Symbol,
    },

    /// Merge CRDT state.
    /// `Merge remote into local.` or `Merge remote's field into local's field.`
    MergeCrdt {
        source: &'a Expr<'a>,
        target: &'a Expr<'a>,
    },

    /// Increment GCounter.
    /// `Increase local's points by 10.`
    IncreaseCrdt {
        object: &'a Expr<'a>,
        field: Symbol,
        amount: &'a Expr<'a>,
    },

    /// Decrement PNCounter (Tally).
    /// `Decrease game's score by 5.`
    DecreaseCrdt {
        object: &'a Expr<'a>,
        field: Symbol,
        amount: &'a Expr<'a>,
    },

    /// Append to SharedSequence (RGA).
    /// `Append "Hello" to doc's lines.`
    AppendToSequence {
        sequence: &'a Expr<'a>,
        value: &'a Expr<'a>,
    },

    /// Resolve MVRegister conflicts.
    /// `Resolve page's title to "Final".`
    ResolveConflict {
        object: &'a Expr<'a>,
        field: Symbol,
        value: &'a Expr<'a>,
    },

    /// Security check - mandatory runtime guard.
    /// `Check that user is admin.`
    /// `Check that user can publish the document.`
    /// Semantics: NEVER optimized out. Panics if condition is false.
    Check {
        /// The subject being checked (e.g., "user")
        subject: Symbol,
        /// The predicate name (e.g., "admin") or action (e.g., "publish")
        predicate: Symbol,
        /// True if this is a capability check (`can [action]`)
        is_capability: bool,
        /// For capabilities: the object being acted on (e.g., "document")
        object: Option<Symbol>,
        /// Original English text for error message
        source_text: String,
        /// Source location for error reporting
        span: crate::token::Span,
    },

    /// Listen on network address.
    /// `Listen on "/ip4/127.0.0.1/tcp/8000".`
    /// Semantics: Bind to address, start accepting connections via libp2p
    Listen {
        address: &'a Expr<'a>,
    },

    /// Connect to remote peer.
    /// `Connect to "/ip4/127.0.0.1/tcp/8000".`
    /// Semantics: Dial peer via libp2p
    ConnectTo {
        address: &'a Expr<'a>,
    },

    /// Create PeerAgent remote handle.
    /// `Let remote be a PeerAgent at "/ip4/127.0.0.1/tcp/8000".`
    /// Semantics: Create handle for remote agent communication
    LetPeerAgent {
        var: Symbol,
        address: &'a Expr<'a>,
    },

    /// Sleep for milliseconds.
    /// `Sleep 1000.` or `Sleep delay.`
    /// Semantics: Pause execution for N milliseconds (async)
    Sleep {
        milliseconds: &'a Expr<'a>,
    },

    /// Sync CRDT variable on topic.
    /// `Sync x on "topic".`
    /// Semantics: Subscribe to GossipSub topic, auto-publish on mutation, auto-merge on receive
    Sync {
        var: Symbol,
        topic: &'a Expr<'a>,
    },

    /// Mount persistent CRDT from journal file.
    /// `Mount counter at "data/counter.journal".`
    /// Semantics: Load or create journal, replay operations to reconstruct state
    Mount {
        /// The variable name for the mounted value
        var: Symbol,
        /// The path expression for the journal file
        path: &'a Expr<'a>,
    },

    // =========================================================================
    // Go-like Concurrency (Green Threads, Channels, Select)
    // =========================================================================

    /// Launch a fire-and-forget task (green thread).
    /// `Launch a task to process(data).`
    /// Semantics: tokio::spawn with no handle capture
    LaunchTask {
        /// The function to call
        function: Symbol,
        /// Arguments to pass
        args: Vec<&'a Expr<'a>>,
    },

    /// Launch a task with handle for control.
    /// `Let worker be Launch a task to process(data).`
    /// Semantics: tokio::spawn returning JoinHandle
    LaunchTaskWithHandle {
        /// Variable to bind the handle
        handle: Symbol,
        /// The function to call
        function: Symbol,
        /// Arguments to pass
        args: Vec<&'a Expr<'a>>,
    },

    /// Create a bounded channel (pipe).
    /// `Let jobs be a new Pipe of Int.`
    /// Semantics: tokio::sync::mpsc::channel(32)
    CreatePipe {
        /// Variable for the pipe
        var: Symbol,
        /// Type of values in the pipe
        element_type: Symbol,
        /// Optional capacity (defaults to 32)
        capacity: Option<u32>,
    },

    /// Blocking send into pipe.
    /// `Send value into pipe.`
    /// Semantics: pipe_tx.send(value).await
    SendPipe {
        /// The value to send
        value: &'a Expr<'a>,
        /// The pipe to send into
        pipe: &'a Expr<'a>,
    },

    /// Blocking receive from pipe.
    /// `Receive x from pipe.`
    /// Semantics: let x = pipe_rx.recv().await
    ReceivePipe {
        /// Variable to bind the received value
        var: Symbol,
        /// The pipe to receive from
        pipe: &'a Expr<'a>,
    },

    /// Non-blocking send (try).
    /// `Try to send value into pipe.`
    /// Semantics: pipe_tx.try_send(value) - returns immediately
    TrySendPipe {
        /// The value to send
        value: &'a Expr<'a>,
        /// The pipe to send into
        pipe: &'a Expr<'a>,
        /// Variable to bind the result (true/false)
        result: Option<Symbol>,
    },

    /// Non-blocking receive (try).
    /// `Try to receive x from pipe.`
    /// Semantics: pipe_rx.try_recv() - returns Option
    TryReceivePipe {
        /// Variable to bind the received value (if any)
        var: Symbol,
        /// The pipe to receive from
        pipe: &'a Expr<'a>,
    },

    /// Cancel a spawned task.
    /// `Stop worker.`
    /// Semantics: handle.abort()
    StopTask {
        /// The handle to cancel
        handle: &'a Expr<'a>,
    },

    /// Select on multiple channels/timeouts.
    /// `Await the first of:`
    ///     `Receive x from ch:`
    ///         `...`
    ///     `After 5 seconds:`
    ///         `...`
    /// Semantics: tokio::select! with auto-cancel
    Select {
        /// The branches to select from
        branches: Vec<SelectBranch<'a>>,
    },

    /// Theorem block.
    /// `## Theorem: Name`
    /// `Given: Premise.`
    /// `Prove: Goal.`
    /// `Proof: Auto.`
    Theorem(TheoremBlock<'a>),

    /// Escape hatch: embed raw foreign code.
    /// `Escape to Rust:` followed by an indented block of raw code.
    ///
    /// Variables from the enclosing LOGOS scope are available in the
    /// escape block as their generated Rust types. The raw code is
    /// emitted verbatim inside a `{ ... }` block in the generated Rust.
    Escape {
        /// Target language ("Rust" for now, forward-compatible with "Python", "WGSL", etc.)
        language: Symbol,
        /// Raw foreign code, captured verbatim with base indentation stripped.
        code: Symbol,
        /// Source span covering the entire escape block (header + body).
        span: crate::token::Span,
    },

    /// Dependency declaration from `## Requires` block.
    /// The "serde" crate version "1.0" with features "derive".
    Require {
        crate_name: Symbol,
        version: Symbol,
        features: Vec<Symbol>,
        span: crate::token::Span,
    },
}

/// A branch in a Select statement.
#[derive(Debug, Clone)]
pub enum SelectBranch<'a> {
    /// Receive from a pipe: `Receive x from ch:`
    Receive {
        var: Symbol,
        pipe: &'a Expr<'a>,
        body: Block<'a>,
    },
    /// Timeout: `After N seconds:` or `After N milliseconds:`
    Timeout {
        milliseconds: &'a Expr<'a>,
        body: Block<'a>,
    },
}

/// Shared expression type for pure computations (LOGOS §15.0.0).
///
/// Expr is used by both LogicExpr (as terms) and Stmt (as values).
/// These are pure computations without side effects.
#[derive(Debug)]
pub enum Expr<'a> {
    /// Literal value: 42, "hello", true, nothing
    Literal(Literal),

    /// Variable reference: x
    Identifier(Symbol),

    /// Binary operation: x plus y
    BinaryOp {
        op: BinaryOpKind,
        left: &'a Expr<'a>,
        right: &'a Expr<'a>,
    },

    /// Unary NOT: "not x" → `!x` (logical for Bool, bitwise for Int)
    Not {
        operand: &'a Expr<'a>,
    },

    /// Function call as expression: f(x, y)
    Call {
        function: Symbol,
        args: Vec<&'a Expr<'a>>,
    },

    /// Dynamic index access: `items at i` (1-indexed).
    Index {
        collection: &'a Expr<'a>,
        index: &'a Expr<'a>,
    },

    /// Dynamic slice access: `items 1 through mid` (1-indexed, inclusive).
    Slice {
        collection: &'a Expr<'a>,
        start: &'a Expr<'a>,
        end: &'a Expr<'a>,
    },

    /// Copy expression: `copy of slice` → slice.to_vec().
    Copy {
        expr: &'a Expr<'a>,
    },

    /// Give expression: `Give x` → transfers ownership, no clone needed.
    /// Used in function calls to explicitly move values.
    Give {
        value: &'a Expr<'a>,
    },

    /// Length expression: `length of items` → items.len().
    Length {
        collection: &'a Expr<'a>,
    },

    /// Set contains: `set contains x` or `x in set`
    Contains {
        collection: &'a Expr<'a>,
        value: &'a Expr<'a>,
    },

    /// Set union: `a union b`
    Union {
        left: &'a Expr<'a>,
        right: &'a Expr<'a>,
    },

    /// Set intersection: `a intersection b`
    Intersection {
        left: &'a Expr<'a>,
        right: &'a Expr<'a>,
    },

    /// Get manifest of a zone.
    /// `the manifest of Zone` → FileSipper::from_zone(&zone).manifest()
    ManifestOf {
        zone: &'a Expr<'a>,
    },

    /// Get chunk at index from a zone.
    /// `the chunk at N in Zone` → FileSipper::from_zone(&zone).get_chunk(N)
    ChunkAt {
        index: &'a Expr<'a>,
        zone: &'a Expr<'a>,
    },

    /// List literal: [1, 2, 3]
    List(Vec<&'a Expr<'a>>),

    /// Tuple literal: (1, "hello", true)
    Tuple(Vec<&'a Expr<'a>>),

    /// Range: 1 to 10 (inclusive)
    Range {
        start: &'a Expr<'a>,
        end: &'a Expr<'a>,
    },

    /// Field access: `p's x` or `the x of p`.
    FieldAccess {
        object: &'a Expr<'a>,
        field: Symbol,
    },

    /// Constructor: `a new Point` or `a new Point with x 10 and y 20`.
    /// Supports generics: `a new Box of Int` and nested types: `a new Seq of (Seq of Int)`
    New {
        type_name: Symbol,
        type_args: Vec<TypeExpr<'a>>,  // Empty for non-generic types - now supports nested types
        init_fields: Vec<(Symbol, &'a Expr<'a>)>,  // Optional field initialization
    },

    /// Enum variant constructor: `a new Circle with radius 10`.
    NewVariant {
        enum_name: Symbol,                      // Shape (resolved from registry)
        variant: Symbol,                        // Circle
        fields: Vec<(Symbol, &'a Expr<'a>)>,    // [(radius, 10)]
    },

    /// Escape hatch expression: raw foreign code that produces a value.
    /// Used in expression position: `Let x: Int be Escape to Rust:`
    Escape {
        language: Symbol,
        code: Symbol,
    },

    /// Option Some: `some 30` → Some(30)
    OptionSome {
        value: &'a Expr<'a>,
    },

    /// Option None: `none` → None
    OptionNone,

    /// Pre-allocation capacity hint wrapping an inner value expression.
    /// `"" with capacity 100` or `a new Seq of Int with capacity n`
    /// Codegen uses with_capacity(); interpreter ignores the hint.
    WithCapacity {
        value: &'a Expr<'a>,
        capacity: &'a Expr<'a>,
    },

    /// Closure expression: `(params) -> body` or `(params) ->:` block body.
    /// Captures variables from the enclosing scope by value (snapshot/clone).
    Closure {
        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
        body: ClosureBody<'a>,
        return_type: Option<&'a TypeExpr<'a>>,
    },

    /// Call an expression that evaluates to a callable value.
    /// `f(x)` where `f` is a variable holding a closure, not a named function.
    CallExpr {
        callee: &'a Expr<'a>,
        args: Vec<&'a Expr<'a>>,
    },

    /// Interpolated string: `"Hello, {name}! Value: {x:.2}"`
    InterpolatedString(Vec<StringPart<'a>>),
}

/// A segment of an interpolated string.
#[derive(Debug, Clone)]
pub enum StringPart<'a> {
    /// Literal text segment
    Literal(Symbol),
    /// Expression with optional format specifier
    Expr {
        value: &'a Expr<'a>,
        format_spec: Option<Symbol>,
        /// Self-documenting debug format: `{var=}` → `"var=42"`
        debug: bool,
    },
}

/// Body of a closure expression.
#[derive(Debug, Clone)]
pub enum ClosureBody<'a> {
    /// Single expression: `(n: Int) -> n * 2`
    Expression(&'a Expr<'a>),
    /// Block of statements: `(n: Int) ->:` followed by indented body
    Block(Block<'a>),
}

/// Literal values in LOGOS.
#[derive(Debug, Clone)]
pub enum Literal {
    /// Integer literal
    Number(i64),
    /// Float literal
    Float(f64),
    /// Text literal
    Text(Symbol),
    /// Boolean literal
    Boolean(bool),
    /// The nothing literal (unit type)
    Nothing,
    /// Character literal
    Char(char),
    /// Duration literal (nanoseconds, signed for negative offsets like "5 minutes early")
    Duration(i64),
    /// Date literal (days since Unix epoch 1970-01-01)
    Date(i32),
    /// Moment literal (nanoseconds since Unix epoch)
    Moment(i64),
    /// Calendar span (months, days) - NOT flattened to seconds
    /// Months and days are kept separate because they're incommensurable.
    Span { months: i32, days: i32 },
    /// Time-of-day literal (nanoseconds from midnight)
    /// Range: 0 to 86_399_999_999_999 (just under 24 hours)
    Time(i64),
}

impl PartialEq for Literal {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Literal::Number(a), Literal::Number(b)) => a == b,
            (Literal::Float(a), Literal::Float(b)) => a.to_bits() == b.to_bits(),
            (Literal::Text(a), Literal::Text(b)) => a == b,
            (Literal::Boolean(a), Literal::Boolean(b)) => a == b,
            (Literal::Nothing, Literal::Nothing) => true,
            (Literal::Char(a), Literal::Char(b)) => a == b,
            (Literal::Duration(a), Literal::Duration(b)) => a == b,
            (Literal::Date(a), Literal::Date(b)) => a == b,
            (Literal::Moment(a), Literal::Moment(b)) => a == b,
            (Literal::Span { months: m1, days: d1 }, Literal::Span { months: m2, days: d2 }) => m1 == m2 && d1 == d2,
            (Literal::Time(a), Literal::Time(b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Literal {}

impl std::hash::Hash for Literal {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::mem::discriminant(self).hash(state);
        match self {
            Literal::Number(n) => n.hash(state),
            Literal::Float(f) => f.to_bits().hash(state),
            Literal::Text(s) => s.hash(state),
            Literal::Boolean(b) => b.hash(state),
            Literal::Nothing => {}
            Literal::Char(c) => c.hash(state),
            Literal::Duration(d) => d.hash(state),
            Literal::Date(d) => d.hash(state),
            Literal::Moment(m) => m.hash(state),
            Literal::Span { months, days } => {
                months.hash(state);
                days.hash(state);
            }
            Literal::Time(t) => t.hash(state),
        }
    }
}