logicaffeine_language/ast/
stmt.rs

1//! Imperative statement AST types for the LOGOS language.
2//!
3//! This module defines statement types for the imperative fragment including:
4//!
5//! - **[`Stmt`]**: Statement variants (let, if, match, while, for, function defs)
6//! - **[`Expr`]**: Imperative expressions (field access, method calls, literals)
7//! - **[`TypeExpr`]**: Type annotations with refinements and generics
8//! - **[`Literal`]**: Literal values (numbers, strings, booleans)
9//! - **[`Block`]**: Statement blocks with optional return expressions
10//!
11//! The imperative AST is used in LOGOS mode for generating executable Rust code.
12
13use std::collections::HashSet;
14
15use super::logic::LogicExpr;
16use super::theorem::TheoremBlock;
17use logicaffeine_base::Symbol;
18
19/// Per-function optimization control flags.
20///
21/// Annotations placed above `## To` disable specific optimization passes
22/// for that function. This gives the programmer explicit control when
23/// an optimization hurts rather than helps (e.g., memoization on a function
24/// whose body is cheaper than a hash lookup).
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub enum OptFlag {
27    /// `## No Memo` — disable auto-memoization (TLS FxHashMap cache)
28    NoMemo,
29    /// `## No TCO` — disable tail-call elimination (loop conversion)
30    NoTCO,
31    /// `## No Peephole` — disable peephole patterns (swap, vec-fill, for-range, etc.)
32    NoPeephole,
33    /// `## No Borrow` — disable readonly/mutable borrow analysis (&[T]/&mut [T] params)
34    NoBorrow,
35    /// `## No Optimize` — disable ALL of the above (master switch)
36    NoOptimize,
37}
38
39/// Type expression for explicit type annotations.
40///
41/// Represents type syntax like:
42/// - `Int` → Primitive(Int)
43/// - `User` → Named(User)
44/// - `List of Int` → Generic { base: List, params: [Primitive(Int)] }
45/// - `List of List of Int` → Generic { base: List, params: [Generic { base: List, params: [Primitive(Int)] }] }
46/// - `Result of Int and Text` → Generic { base: Result, params: [Primitive(Int), Primitive(Text)] }
47#[derive(Debug, Clone)]
48pub enum TypeExpr<'a> {
49    /// Primitive type: Int, Nat, Text, Bool
50    Primitive(Symbol),
51    /// Named type (user-defined): User, Point
52    Named(Symbol),
53    /// Generic type: List of Int, Option of Text, Result of Int and Text
54    Generic {
55        base: Symbol,
56        params: &'a [TypeExpr<'a>],
57    },
58    /// Function type: fn(A, B) -> C (for higher-order functions)
59    Function {
60        inputs: &'a [TypeExpr<'a>],
61        output: &'a TypeExpr<'a>,
62    },
63    /// Refinement type with predicate constraint.
64    /// Example: `Int where it > 0`
65    Refinement {
66        /// The base type being refined
67        base: &'a TypeExpr<'a>,
68        /// The bound variable (usually "it")
69        var: Symbol,
70        /// The predicate constraint (from Logic Kernel)
71        predicate: &'a LogicExpr<'a>,
72    },
73    /// Persistent storage wrapper type.
74    /// Example: `Persistent Counter`
75    /// Semantics: Wraps a Shared type with journal-backed storage
76    Persistent {
77        /// The inner type (must be a Shared/CRDT type)
78        inner: &'a TypeExpr<'a>,
79    },
80}
81
82/// Source for Read statements.
83#[derive(Debug, Clone, Copy)]
84pub enum ReadSource<'a> {
85    /// Read from console (stdin)
86    Console,
87    /// Read from file at given path
88    File(&'a Expr<'a>),
89}
90
91/// Pattern for loop variable binding.
92/// Supports single identifiers and tuple destructuring for Map iteration.
93#[derive(Debug, Clone)]
94pub enum Pattern {
95    /// Single identifier: `Repeat for x in list`
96    Identifier(Symbol),
97    /// Tuple destructuring: `Repeat for (k, v) in map`
98    Tuple(Vec<Symbol>),
99}
100
101/// Binary operation kinds for imperative expressions.
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub enum BinaryOpKind {
104    Add,
105    Subtract,
106    Multiply,
107    Divide,
108    Modulo,
109    Eq,
110    NotEq,
111    Lt,
112    Gt,
113    LtEq,
114    GtEq,
115    // Logical/bitwise operators — type-aware in codegen (&&/|| for Bool, &/| for Int)
116    And,
117    Or,
118    /// String concatenation ("X combined with Y")
119    Concat,
120    /// Bitwise XOR: "x xor y" → `x ^ y`
121    BitXor,
122    /// Left shift: "x shifted left by y" → `x << y`
123    Shl,
124    /// Right shift: "x shifted right by y" → `x >> y`
125    Shr,
126}
127
128/// Block is a sequence of statements.
129pub type Block<'a> = &'a [Stmt<'a>];
130
131/// Match arm for pattern matching in Inspect statements.
132#[derive(Debug, Clone)]
133pub struct MatchArm<'a> {
134    pub enum_name: Option<Symbol>,          // The enum type (e.g., Shape)
135    pub variant: Option<Symbol>,            // None = Otherwise (wildcard)
136    pub bindings: Vec<(Symbol, Symbol)>,    // (field_name, binding_name)
137    pub body: Block<'a>,
138}
139
140/// Imperative statement AST (LOGOS §15.0.0).
141///
142/// Stmt is the primary AST node for imperative code blocks like `## Main`
143/// and function bodies. The Assert variant bridges to the Logic Kernel.
144#[derive(Debug, Clone)]
145pub enum Stmt<'a> {
146    /// Variable binding: `Let x be 5.` or `Let x: Int be 5.`
147    Let {
148        var: Symbol,
149        ty: Option<&'a TypeExpr<'a>>,
150        value: &'a Expr<'a>,
151        mutable: bool,
152    },
153
154    /// Mutation: `Set x to 10.`
155    Set {
156        target: Symbol,
157        value: &'a Expr<'a>,
158    },
159
160    /// Function call as statement: `Call process with data.`
161    Call {
162        function: Symbol,
163        args: Vec<&'a Expr<'a>>,
164    },
165
166    /// Conditional: `If condition: ... Otherwise: ...`
167    If {
168        cond: &'a Expr<'a>,
169        then_block: Block<'a>,
170        else_block: Option<Block<'a>>,
171    },
172
173    /// Loop: `While condition: ...` or `While condition (decreasing expr): ...`
174    While {
175        cond: &'a Expr<'a>,
176        body: Block<'a>,
177        /// Optional decreasing variant for termination proof.
178        decreasing: Option<&'a Expr<'a>>,
179    },
180
181    /// Iteration: `Repeat for x in list: ...` or `Repeat for i from 1 to 10: ...`
182    Repeat {
183        pattern: Pattern,  // Changed from `var: Symbol` to support tuple destructuring
184        iterable: &'a Expr<'a>,
185        body: Block<'a>,
186    },
187
188    /// Return: `Return x.` or `Return.`
189    Return {
190        value: Option<&'a Expr<'a>>,
191    },
192
193    /// Break: `Break.` — exits the innermost while loop.
194    Break,
195
196    /// Bridge to Logic Kernel: `Assert that P.`
197    Assert {
198        proposition: &'a LogicExpr<'a>,
199    },
200
201    /// Documented assertion with justification.
202    /// `Trust that P because "reason".`
203    /// Semantics: Documented runtime check that could be verified statically.
204    Trust {
205        proposition: &'a LogicExpr<'a>,
206        justification: Symbol,
207    },
208
209    /// Runtime assertion with imperative condition
210    /// `Assert that condition.` (for imperative mode)
211    RuntimeAssert {
212        condition: &'a Expr<'a>,
213    },
214
215    /// Ownership transfer (move): `Give x to processor.`
216    /// Semantics: Move ownership of `object` to `recipient`.
217    Give {
218        object: &'a Expr<'a>,
219        recipient: &'a Expr<'a>,
220    },
221
222    /// Immutable borrow: `Show x to console.`
223    /// Semantics: Immutable borrow of `object` passed to `recipient`.
224    Show {
225        object: &'a Expr<'a>,
226        recipient: &'a Expr<'a>,
227    },
228
229    /// Field mutation: `Set p's x to 10.`
230    SetField {
231        object: &'a Expr<'a>,
232        field: Symbol,
233        value: &'a Expr<'a>,
234    },
235
236    /// Struct definition for codegen.
237    StructDef {
238        name: Symbol,
239        fields: Vec<(Symbol, Symbol, bool)>, // (name, type_name, is_public)
240        is_portable: bool,                    // Derives Serialize/Deserialize
241    },
242
243    /// Function definition.
244    FunctionDef {
245        name: Symbol,
246        /// Generic type parameters: empty for monomorphic functions, e.g. `[T, U]` for polymorphic.
247        generics: Vec<Symbol>,
248        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
249        body: Block<'a>,
250        return_type: Option<&'a TypeExpr<'a>>,
251        is_native: bool,
252        /// Rust path for user-defined native functions (e.g., "reqwest::blocking::get").
253        /// None for system native functions (read, write, etc.) which use map_native_function().
254        native_path: Option<Symbol>,
255        /// Whether this function is exported for FFI (C ABI or WASM).
256        is_exported: bool,
257        /// Export target: None = C ABI (#[no_mangle] extern "C"), Some("wasm") = #[wasm_bindgen].
258        export_target: Option<Symbol>,
259        /// Per-function optimization flags from `## No <X>` annotations.
260        opt_flags: HashSet<OptFlag>,
261    },
262
263    /// Pattern matching on sum types.
264    Inspect {
265        target: &'a Expr<'a>,
266        arms: Vec<MatchArm<'a>>,
267        has_otherwise: bool,            // For exhaustiveness tracking
268    },
269
270    /// Push to collection: `Push x to items.`
271    Push {
272        value: &'a Expr<'a>,
273        collection: &'a Expr<'a>,
274    },
275
276    /// Pop from collection: `Pop from items.` or `Pop from items into y.`
277    Pop {
278        collection: &'a Expr<'a>,
279        into: Option<Symbol>,
280    },
281
282    /// Add to set: `Add x to set.`
283    Add {
284        value: &'a Expr<'a>,
285        collection: &'a Expr<'a>,
286    },
287
288    /// Remove from set: `Remove x from set.`
289    Remove {
290        value: &'a Expr<'a>,
291        collection: &'a Expr<'a>,
292    },
293
294    /// Index assignment: `Set item N of X to Y.`
295    SetIndex {
296        collection: &'a Expr<'a>,
297        index: &'a Expr<'a>,
298        value: &'a Expr<'a>,
299    },
300
301    /// Memory arena block (Zone).
302    /// "Inside a new zone called 'Scratch':"
303    /// "Inside a zone called 'Buffer' of size 1 MB:"
304    /// "Inside a zone called 'Data' mapped from 'file.bin':"
305    Zone {
306        /// The variable name for the arena handle (e.g., "Scratch")
307        name: Symbol,
308        /// Optional pre-allocated capacity in bytes (Heap zones only)
309        capacity: Option<usize>,
310        /// Optional file path for memory-mapped zones (Mapped zones only)
311        source_file: Option<Symbol>,
312        /// The code block executed within this memory context
313        body: Block<'a>,
314    },
315
316    /// Concurrent execution block (async, I/O-bound).
317    /// "Attempt all of the following:"
318    /// Semantics: All tasks run concurrently via tokio::join!
319    /// Best for: network requests, file I/O, waiting operations
320    Concurrent {
321        /// The statements to execute concurrently
322        tasks: Block<'a>,
323    },
324
325    /// Parallel execution block (CPU-bound).
326    /// "Simultaneously:"
327    /// Semantics: True parallelism via rayon::join or thread::spawn
328    /// Best for: computation, data processing, number crunching
329    Parallel {
330        /// The statements to execute in parallel
331        tasks: Block<'a>,
332    },
333
334    /// Read from console or file.
335    /// `Read input from the console.` or `Read data from file "path.txt".`
336    ReadFrom {
337        var: Symbol,
338        source: ReadSource<'a>,
339    },
340
341    /// Write to file.
342    /// `Write "content" to file "output.txt".`
343    WriteFile {
344        content: &'a Expr<'a>,
345        path: &'a Expr<'a>,
346    },
347
348    /// Spawn an agent.
349    /// `Spawn a Worker called "w1".`
350    Spawn {
351        agent_type: Symbol,
352        name: Symbol,
353    },
354
355    /// Send message to agent.
356    /// `Send Ping to "agent".`
357    SendMessage {
358        message: &'a Expr<'a>,
359        destination: &'a Expr<'a>,
360    },
361
362    /// Await response from agent.
363    /// `Await response from "agent" into result.`
364    AwaitMessage {
365        source: &'a Expr<'a>,
366        into: Symbol,
367    },
368
369    /// Merge CRDT state.
370    /// `Merge remote into local.` or `Merge remote's field into local's field.`
371    MergeCrdt {
372        source: &'a Expr<'a>,
373        target: &'a Expr<'a>,
374    },
375
376    /// Increment GCounter.
377    /// `Increase local's points by 10.`
378    IncreaseCrdt {
379        object: &'a Expr<'a>,
380        field: Symbol,
381        amount: &'a Expr<'a>,
382    },
383
384    /// Decrement PNCounter (Tally).
385    /// `Decrease game's score by 5.`
386    DecreaseCrdt {
387        object: &'a Expr<'a>,
388        field: Symbol,
389        amount: &'a Expr<'a>,
390    },
391
392    /// Append to SharedSequence (RGA).
393    /// `Append "Hello" to doc's lines.`
394    AppendToSequence {
395        sequence: &'a Expr<'a>,
396        value: &'a Expr<'a>,
397    },
398
399    /// Resolve MVRegister conflicts.
400    /// `Resolve page's title to "Final".`
401    ResolveConflict {
402        object: &'a Expr<'a>,
403        field: Symbol,
404        value: &'a Expr<'a>,
405    },
406
407    /// Security check - mandatory runtime guard.
408    /// `Check that user is admin.`
409    /// `Check that user can publish the document.`
410    /// Semantics: NEVER optimized out. Panics if condition is false.
411    Check {
412        /// The subject being checked (e.g., "user")
413        subject: Symbol,
414        /// The predicate name (e.g., "admin") or action (e.g., "publish")
415        predicate: Symbol,
416        /// True if this is a capability check (`can [action]`)
417        is_capability: bool,
418        /// For capabilities: the object being acted on (e.g., "document")
419        object: Option<Symbol>,
420        /// Original English text for error message
421        source_text: String,
422        /// Source location for error reporting
423        span: crate::token::Span,
424    },
425
426    /// Listen on network address.
427    /// `Listen on "/ip4/127.0.0.1/tcp/8000".`
428    /// Semantics: Bind to address, start accepting connections via libp2p
429    Listen {
430        address: &'a Expr<'a>,
431    },
432
433    /// Connect to remote peer.
434    /// `Connect to "/ip4/127.0.0.1/tcp/8000".`
435    /// Semantics: Dial peer via libp2p
436    ConnectTo {
437        address: &'a Expr<'a>,
438    },
439
440    /// Create PeerAgent remote handle.
441    /// `Let remote be a PeerAgent at "/ip4/127.0.0.1/tcp/8000".`
442    /// Semantics: Create handle for remote agent communication
443    LetPeerAgent {
444        var: Symbol,
445        address: &'a Expr<'a>,
446    },
447
448    /// Sleep for milliseconds.
449    /// `Sleep 1000.` or `Sleep delay.`
450    /// Semantics: Pause execution for N milliseconds (async)
451    Sleep {
452        milliseconds: &'a Expr<'a>,
453    },
454
455    /// Sync CRDT variable on topic.
456    /// `Sync x on "topic".`
457    /// Semantics: Subscribe to GossipSub topic, auto-publish on mutation, auto-merge on receive
458    Sync {
459        var: Symbol,
460        topic: &'a Expr<'a>,
461    },
462
463    /// Mount persistent CRDT from journal file.
464    /// `Mount counter at "data/counter.journal".`
465    /// Semantics: Load or create journal, replay operations to reconstruct state
466    Mount {
467        /// The variable name for the mounted value
468        var: Symbol,
469        /// The path expression for the journal file
470        path: &'a Expr<'a>,
471    },
472
473    // =========================================================================
474    // Go-like Concurrency (Green Threads, Channels, Select)
475    // =========================================================================
476
477    /// Launch a fire-and-forget task (green thread).
478    /// `Launch a task to process(data).`
479    /// Semantics: tokio::spawn with no handle capture
480    LaunchTask {
481        /// The function to call
482        function: Symbol,
483        /// Arguments to pass
484        args: Vec<&'a Expr<'a>>,
485    },
486
487    /// Launch a task with handle for control.
488    /// `Let worker be Launch a task to process(data).`
489    /// Semantics: tokio::spawn returning JoinHandle
490    LaunchTaskWithHandle {
491        /// Variable to bind the handle
492        handle: Symbol,
493        /// The function to call
494        function: Symbol,
495        /// Arguments to pass
496        args: Vec<&'a Expr<'a>>,
497    },
498
499    /// Create a bounded channel (pipe).
500    /// `Let jobs be a new Pipe of Int.`
501    /// Semantics: tokio::sync::mpsc::channel(32)
502    CreatePipe {
503        /// Variable for the pipe
504        var: Symbol,
505        /// Type of values in the pipe
506        element_type: Symbol,
507        /// Optional capacity (defaults to 32)
508        capacity: Option<u32>,
509    },
510
511    /// Blocking send into pipe.
512    /// `Send value into pipe.`
513    /// Semantics: pipe_tx.send(value).await
514    SendPipe {
515        /// The value to send
516        value: &'a Expr<'a>,
517        /// The pipe to send into
518        pipe: &'a Expr<'a>,
519    },
520
521    /// Blocking receive from pipe.
522    /// `Receive x from pipe.`
523    /// Semantics: let x = pipe_rx.recv().await
524    ReceivePipe {
525        /// Variable to bind the received value
526        var: Symbol,
527        /// The pipe to receive from
528        pipe: &'a Expr<'a>,
529    },
530
531    /// Non-blocking send (try).
532    /// `Try to send value into pipe.`
533    /// Semantics: pipe_tx.try_send(value) - returns immediately
534    TrySendPipe {
535        /// The value to send
536        value: &'a Expr<'a>,
537        /// The pipe to send into
538        pipe: &'a Expr<'a>,
539        /// Variable to bind the result (true/false)
540        result: Option<Symbol>,
541    },
542
543    /// Non-blocking receive (try).
544    /// `Try to receive x from pipe.`
545    /// Semantics: pipe_rx.try_recv() - returns Option
546    TryReceivePipe {
547        /// Variable to bind the received value (if any)
548        var: Symbol,
549        /// The pipe to receive from
550        pipe: &'a Expr<'a>,
551    },
552
553    /// Cancel a spawned task.
554    /// `Stop worker.`
555    /// Semantics: handle.abort()
556    StopTask {
557        /// The handle to cancel
558        handle: &'a Expr<'a>,
559    },
560
561    /// Select on multiple channels/timeouts.
562    /// `Await the first of:`
563    ///     `Receive x from ch:`
564    ///         `...`
565    ///     `After 5 seconds:`
566    ///         `...`
567    /// Semantics: tokio::select! with auto-cancel
568    Select {
569        /// The branches to select from
570        branches: Vec<SelectBranch<'a>>,
571    },
572
573    /// Theorem block.
574    /// `## Theorem: Name`
575    /// `Given: Premise.`
576    /// `Prove: Goal.`
577    /// `Proof: Auto.`
578    Theorem(TheoremBlock<'a>),
579
580    /// Escape hatch: embed raw foreign code.
581    /// `Escape to Rust:` followed by an indented block of raw code.
582    ///
583    /// Variables from the enclosing LOGOS scope are available in the
584    /// escape block as their generated Rust types. The raw code is
585    /// emitted verbatim inside a `{ ... }` block in the generated Rust.
586    Escape {
587        /// Target language ("Rust" for now, forward-compatible with "Python", "WGSL", etc.)
588        language: Symbol,
589        /// Raw foreign code, captured verbatim with base indentation stripped.
590        code: Symbol,
591        /// Source span covering the entire escape block (header + body).
592        span: crate::token::Span,
593    },
594
595    /// Dependency declaration from `## Requires` block.
596    /// The "serde" crate version "1.0" with features "derive".
597    Require {
598        crate_name: Symbol,
599        version: Symbol,
600        features: Vec<Symbol>,
601        span: crate::token::Span,
602    },
603}
604
605/// A branch in a Select statement.
606#[derive(Debug, Clone)]
607pub enum SelectBranch<'a> {
608    /// Receive from a pipe: `Receive x from ch:`
609    Receive {
610        var: Symbol,
611        pipe: &'a Expr<'a>,
612        body: Block<'a>,
613    },
614    /// Timeout: `After N seconds:` or `After N milliseconds:`
615    Timeout {
616        milliseconds: &'a Expr<'a>,
617        body: Block<'a>,
618    },
619}
620
621/// Shared expression type for pure computations (LOGOS §15.0.0).
622///
623/// Expr is used by both LogicExpr (as terms) and Stmt (as values).
624/// These are pure computations without side effects.
625#[derive(Debug)]
626pub enum Expr<'a> {
627    /// Literal value: 42, "hello", true, nothing
628    Literal(Literal),
629
630    /// Variable reference: x
631    Identifier(Symbol),
632
633    /// Binary operation: x plus y
634    BinaryOp {
635        op: BinaryOpKind,
636        left: &'a Expr<'a>,
637        right: &'a Expr<'a>,
638    },
639
640    /// Unary NOT: "not x" → `!x` (logical for Bool, bitwise for Int)
641    Not {
642        operand: &'a Expr<'a>,
643    },
644
645    /// Function call as expression: f(x, y)
646    Call {
647        function: Symbol,
648        args: Vec<&'a Expr<'a>>,
649    },
650
651    /// Dynamic index access: `items at i` (1-indexed).
652    Index {
653        collection: &'a Expr<'a>,
654        index: &'a Expr<'a>,
655    },
656
657    /// Dynamic slice access: `items 1 through mid` (1-indexed, inclusive).
658    Slice {
659        collection: &'a Expr<'a>,
660        start: &'a Expr<'a>,
661        end: &'a Expr<'a>,
662    },
663
664    /// Copy expression: `copy of slice` → slice.to_vec().
665    Copy {
666        expr: &'a Expr<'a>,
667    },
668
669    /// Give expression: `Give x` → transfers ownership, no clone needed.
670    /// Used in function calls to explicitly move values.
671    Give {
672        value: &'a Expr<'a>,
673    },
674
675    /// Length expression: `length of items` → items.len().
676    Length {
677        collection: &'a Expr<'a>,
678    },
679
680    /// Set contains: `set contains x` or `x in set`
681    Contains {
682        collection: &'a Expr<'a>,
683        value: &'a Expr<'a>,
684    },
685
686    /// Set union: `a union b`
687    Union {
688        left: &'a Expr<'a>,
689        right: &'a Expr<'a>,
690    },
691
692    /// Set intersection: `a intersection b`
693    Intersection {
694        left: &'a Expr<'a>,
695        right: &'a Expr<'a>,
696    },
697
698    /// Get manifest of a zone.
699    /// `the manifest of Zone` → FileSipper::from_zone(&zone).manifest()
700    ManifestOf {
701        zone: &'a Expr<'a>,
702    },
703
704    /// Get chunk at index from a zone.
705    /// `the chunk at N in Zone` → FileSipper::from_zone(&zone).get_chunk(N)
706    ChunkAt {
707        index: &'a Expr<'a>,
708        zone: &'a Expr<'a>,
709    },
710
711    /// List literal: [1, 2, 3]
712    List(Vec<&'a Expr<'a>>),
713
714    /// Tuple literal: (1, "hello", true)
715    Tuple(Vec<&'a Expr<'a>>),
716
717    /// Range: 1 to 10 (inclusive)
718    Range {
719        start: &'a Expr<'a>,
720        end: &'a Expr<'a>,
721    },
722
723    /// Field access: `p's x` or `the x of p`.
724    FieldAccess {
725        object: &'a Expr<'a>,
726        field: Symbol,
727    },
728
729    /// Constructor: `a new Point` or `a new Point with x 10 and y 20`.
730    /// Supports generics: `a new Box of Int` and nested types: `a new Seq of (Seq of Int)`
731    New {
732        type_name: Symbol,
733        type_args: Vec<TypeExpr<'a>>,  // Empty for non-generic types - now supports nested types
734        init_fields: Vec<(Symbol, &'a Expr<'a>)>,  // Optional field initialization
735    },
736
737    /// Enum variant constructor: `a new Circle with radius 10`.
738    NewVariant {
739        enum_name: Symbol,                      // Shape (resolved from registry)
740        variant: Symbol,                        // Circle
741        fields: Vec<(Symbol, &'a Expr<'a>)>,    // [(radius, 10)]
742    },
743
744    /// Escape hatch expression: raw foreign code that produces a value.
745    /// Used in expression position: `Let x: Int be Escape to Rust:`
746    Escape {
747        language: Symbol,
748        code: Symbol,
749    },
750
751    /// Option Some: `some 30` → Some(30)
752    OptionSome {
753        value: &'a Expr<'a>,
754    },
755
756    /// Option None: `none` → None
757    OptionNone,
758
759    /// Pre-allocation capacity hint wrapping an inner value expression.
760    /// `"" with capacity 100` or `a new Seq of Int with capacity n`
761    /// Codegen uses with_capacity(); interpreter ignores the hint.
762    WithCapacity {
763        value: &'a Expr<'a>,
764        capacity: &'a Expr<'a>,
765    },
766
767    /// Closure expression: `(params) -> body` or `(params) ->:` block body.
768    /// Captures variables from the enclosing scope by value (snapshot/clone).
769    Closure {
770        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
771        body: ClosureBody<'a>,
772        return_type: Option<&'a TypeExpr<'a>>,
773    },
774
775    /// Call an expression that evaluates to a callable value.
776    /// `f(x)` where `f` is a variable holding a closure, not a named function.
777    CallExpr {
778        callee: &'a Expr<'a>,
779        args: Vec<&'a Expr<'a>>,
780    },
781
782    /// Interpolated string: `"Hello, {name}! Value: {x:.2}"`
783    InterpolatedString(Vec<StringPart<'a>>),
784}
785
786/// A segment of an interpolated string.
787#[derive(Debug, Clone)]
788pub enum StringPart<'a> {
789    /// Literal text segment
790    Literal(Symbol),
791    /// Expression with optional format specifier
792    Expr {
793        value: &'a Expr<'a>,
794        format_spec: Option<Symbol>,
795        /// Self-documenting debug format: `{var=}` → `"var=42"`
796        debug: bool,
797    },
798}
799
800/// Body of a closure expression.
801#[derive(Debug, Clone)]
802pub enum ClosureBody<'a> {
803    /// Single expression: `(n: Int) -> n * 2`
804    Expression(&'a Expr<'a>),
805    /// Block of statements: `(n: Int) ->:` followed by indented body
806    Block(Block<'a>),
807}
808
809/// Literal values in LOGOS.
810#[derive(Debug, Clone)]
811pub enum Literal {
812    /// Integer literal
813    Number(i64),
814    /// Float literal
815    Float(f64),
816    /// Text literal
817    Text(Symbol),
818    /// Boolean literal
819    Boolean(bool),
820    /// The nothing literal (unit type)
821    Nothing,
822    /// Character literal
823    Char(char),
824    /// Duration literal (nanoseconds, signed for negative offsets like "5 minutes early")
825    Duration(i64),
826    /// Date literal (days since Unix epoch 1970-01-01)
827    Date(i32),
828    /// Moment literal (nanoseconds since Unix epoch)
829    Moment(i64),
830    /// Calendar span (months, days) - NOT flattened to seconds
831    /// Months and days are kept separate because they're incommensurable.
832    Span { months: i32, days: i32 },
833    /// Time-of-day literal (nanoseconds from midnight)
834    /// Range: 0 to 86_399_999_999_999 (just under 24 hours)
835    Time(i64),
836}
837
838impl PartialEq for Literal {
839    fn eq(&self, other: &Self) -> bool {
840        match (self, other) {
841            (Literal::Number(a), Literal::Number(b)) => a == b,
842            (Literal::Float(a), Literal::Float(b)) => a.to_bits() == b.to_bits(),
843            (Literal::Text(a), Literal::Text(b)) => a == b,
844            (Literal::Boolean(a), Literal::Boolean(b)) => a == b,
845            (Literal::Nothing, Literal::Nothing) => true,
846            (Literal::Char(a), Literal::Char(b)) => a == b,
847            (Literal::Duration(a), Literal::Duration(b)) => a == b,
848            (Literal::Date(a), Literal::Date(b)) => a == b,
849            (Literal::Moment(a), Literal::Moment(b)) => a == b,
850            (Literal::Span { months: m1, days: d1 }, Literal::Span { months: m2, days: d2 }) => m1 == m2 && d1 == d2,
851            (Literal::Time(a), Literal::Time(b)) => a == b,
852            _ => false,
853        }
854    }
855}
856
857impl Eq for Literal {}
858
859impl std::hash::Hash for Literal {
860    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
861        std::mem::discriminant(self).hash(state);
862        match self {
863            Literal::Number(n) => n.hash(state),
864            Literal::Float(f) => f.to_bits().hash(state),
865            Literal::Text(s) => s.hash(state),
866            Literal::Boolean(b) => b.hash(state),
867            Literal::Nothing => {}
868            Literal::Char(c) => c.hash(state),
869            Literal::Duration(d) => d.hash(state),
870            Literal::Date(d) => d.hash(state),
871            Literal::Moment(m) => m.hash(state),
872            Literal::Span { months, days } => {
873                months.hash(state);
874                days.hash(state);
875            }
876            Literal::Time(t) => t.hash(state),
877        }
878    }
879}