Skip to main content

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 super::axiom::{AxiomBlock, TheoryBlock};
14use super::definition::DefinitionBlock;
15use super::logic::LogicExpr;
16use super::theorem::TheoremBlock;
17use logicaffeine_base::Symbol;
18
19/// Type expression for explicit type annotations.
20///
21/// Represents type syntax like:
22/// - `Int` → Primitive(Int)
23/// - `User` → Named(User)
24/// - `List of Int` → Generic { base: List, params: [Primitive(Int)] }
25/// - `List of List of Int` → Generic { base: List, params: [Generic { base: List, params: [Primitive(Int)] }] }
26/// - `Result of Int and Text` → Generic { base: Result, params: [Primitive(Int), Primitive(Text)] }
27#[derive(Debug, Clone)]
28pub enum TypeExpr<'a> {
29    /// Primitive type: Int, Nat, Text, Bool
30    Primitive(Symbol),
31    /// Named type (user-defined): User, Point
32    Named(Symbol),
33    /// Generic type: List of Int, Option of Text, Result of Int and Text
34    Generic {
35        base: Symbol,
36        params: &'a [TypeExpr<'a>],
37    },
38    /// Function type: fn(A, B) -> C (for higher-order functions)
39    Function {
40        inputs: &'a [TypeExpr<'a>],
41        output: &'a TypeExpr<'a>,
42    },
43    /// Refinement type with predicate constraint.
44    /// Example: `Int where it > 0`
45    Refinement {
46        /// The base type being refined
47        base: &'a TypeExpr<'a>,
48        /// The bound variable (usually "it")
49        var: Symbol,
50        /// The predicate constraint (from Logic Kernel)
51        predicate: &'a LogicExpr<'a>,
52    },
53    /// Persistent storage wrapper type.
54    /// Example: `Persistent Counter`
55    /// Semantics: Wraps a Shared type with journal-backed storage
56    Persistent {
57        /// The inner type (must be a Shared/CRDT type)
58        inner: &'a TypeExpr<'a>,
59    },
60    /// Mutable-parameter marker (Mutable Value Semantics escape hatch).
61    /// Example: `## To addItem (items: mutable Seq of Int):` →
62    /// `Mutable { inner: Generic { List, [Int] } }`.
63    /// Semantics: under value semantics collections pass by value by default; a
64    /// `Mutable` parameter passes by reference so the callee's mutations are
65    /// visible to the caller (the explicit, opt-in form of the void-mutate
66    /// idiom). Carried on the parameter's type so the `(Symbol, &TypeExpr)`
67    /// parameter representation is unchanged. For every purpose other than the
68    /// parameter-passing convention, `Mutable { inner }` behaves as `inner`.
69    Mutable {
70        /// The underlying parameter type.
71        inner: &'a TypeExpr<'a>,
72    },
73}
74
75/// Source for Read statements.
76#[derive(Debug, Clone, Copy)]
77pub enum ReadSource<'a> {
78    /// Read from console (stdin)
79    Console,
80    /// Read from file at given path
81    File(&'a Expr<'a>),
82}
83
84/// Pattern for loop variable binding.
85/// Supports single identifiers and tuple destructuring for Map iteration.
86#[derive(Debug, Clone)]
87pub enum Pattern {
88    /// Single identifier: `Repeat for x in list`
89    Identifier(Symbol),
90    /// Tuple destructuring: `Repeat for (k, v) in map`
91    Tuple(Vec<Symbol>),
92}
93
94/// Binary operation kinds for imperative expressions.
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
96pub enum BinaryOpKind {
97    Add,
98    Subtract,
99    Multiply,
100    /// `a ** b` — exponentiation. Integer power is EXACT (promotes to BigInt on
101    /// overflow); a Float operand uses `powf`; a negative integer exponent is a
102    /// loud error. Binds tighter than `* / %`, right-associative.
103    Pow,
104    Divide,
105    /// EXACT division — the type-directed sibling of [`BinaryOpKind::Divide`].
106    /// `Divide` floors (`7 / 2 → 3`, the integer default); `ExactDivide` keeps the
107    /// quotient exact (`7 / 2 → 7/2`, a `Rational`). The `resolve_divisions` pass
108    /// rewrites `Divide → ExactDivide` only where the result flows into a `Rational`
109    /// context, so existing (floor) code is untouched.
110    ExactDivide,
111    /// `a // b` — FLOOR division, rounding toward negative infinity (`-7 // 2 → -4`).
112    /// Distinct from [`BinaryOpKind::Divide`], which truncates toward zero (`-7 / 2 →
113    /// -3`); the two agree when the operands share a sign. Exact (promotes to BigInt),
114    /// integer-producing, and immune to the `resolve_divisions` Rational rewrite — the
115    /// explicit spelling for "give me the floored integer quotient."
116    FloorDivide,
117    Modulo,
118    Eq,
119    NotEq,
120    Lt,
121    Gt,
122    LtEq,
123    GtEq,
124    // Logical/bitwise operators — type-aware in codegen (&&/|| for Bool, &/| for Int)
125    And,
126    Or,
127    /// String concatenation ("X combined with Y")
128    Concat,
129    /// Sequence concatenation ("A followed by B") — merge two sequences into one.
130    SeqConcat,
131    /// Tolerant float comparison ("a is approximately b") — Python-isclose
132    /// semantics (rel 1e-9, abs floor 1e-12). `==` stays IEEE bit-exact;
133    /// this is the EXPLICIT spelling for near-equality.
134    ApproxEq,
135    /// Bitwise XOR: `x ^ y` (the word `xor` is the English spelling); on
136    /// Sets, symmetric difference.
137    BitXor,
138    /// Bitwise AND: `x & y`; on Sets, intersection.
139    BitAnd,
140    /// Bitwise OR: `x | y`; on Sets, union.
141    BitOr,
142    /// Left shift: "x shifted left by y" → `x << y`
143    Shl,
144    /// Right shift: "x shifted right by y" → `x >> y`
145    Shr,
146}
147
148/// Block is a sequence of statements.
149pub type Block<'a> = &'a [Stmt<'a>];
150
151/// Match arm for pattern matching in Inspect statements.
152#[derive(Debug, Clone)]
153pub struct MatchArm<'a> {
154    pub enum_name: Option<Symbol>,          // The enum type (e.g., Shape)
155    pub variant: Option<Symbol>,            // None = Otherwise (wildcard)
156    pub bindings: Vec<(Symbol, Symbol)>,    // (field_name, binding_name)
157    pub body: Block<'a>,
158}
159
160/// The wire compression codec a `Send compressed [with <codec>]` selects. The
161/// transpiler maps this to the runtime's wire codec; bare `compressed` = `Deflate`.
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
163pub enum CompressionCodec {
164    /// DEFLATE — the balanced default (bare `Send compressed`).
165    Deflate,
166    /// LZ4 — near-memcpy speed, lighter ratio.
167    Lz4,
168    /// Zstandard — the best ratio.
169    Zstd,
170}
171
172/// The wire LAYOUT a `Send` modifier picks — the size↔speed dial the sender chooses for
173/// their link. The transpiler maps this to the runtime's numeric codec. The sender knows
174/// their use case; this lets them express it in one word.
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
176pub enum SendLayout {
177    /// `compact` / `small` — smallest wire (LEB128 varint). For a bandwidth-bound link
178    /// (mobile, WAN, metered). This is also the default when no layout word is given.
179    Compact,
180    /// `fast` / `quickly` — fastest decode (fixed-width memcpy, zero parse). For a
181    /// latency-bound / fat link (LAN, datacenter, RDMA).
182    Fast,
183    /// `packed` — varint size with SIMD group-varint decode; the balanced middle.
184    Packed,
185    /// `smallest` / `best` — turn on the per-column compression menu (delta /
186    /// delta-of-delta / frame-of-reference / run-length / dictionary), auto-selecting
187    /// each column's smallest form and never exceeding plain varint. For a
188    /// bandwidth-bound link where CPU is cheap relative to bytes.
189    Smallest,
190    /// `redundant` / `tough` — forward error correction: the message is split into
191    /// Reed-Solomon shards and each is published as its own packet, so a receiver
192    /// reconstructs the exact message from any K even after some are lost. For a lossy
193    /// / one-way link (UDP, multicast, BLE, LoRa) where retransmit is impossible.
194    Redundant,
195}
196
197/// Backing-file source for a memory-mapped zone (`… mapped from <here>`).
198/// `Inside a zone called "D" mapped from "f.bin"` is [`ZoneSource::Literal`];
199/// `… mapped from path` (a runtime `Text` variable) is [`ZoneSource::Variable`].
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub enum ZoneSource {
202    /// A string-literal path baked into the source.
203    Literal(Symbol),
204    /// A variable holding the path, resolved at runtime.
205    Variable(Symbol),
206}
207
208/// Imperative statement AST (LOGOS §15.0.0).
209///
210/// Stmt is the primary AST node for imperative code blocks like `## Main`
211/// and function bodies. The Assert variant bridges to the Logic Kernel.
212/// Which end of the shared pad this peer draws from, for the PNP one-time-pad tier.
213/// `as initiator` sends on the first directional half; `as responder` sends on the second.
214#[derive(Debug, Clone, Copy, PartialEq, Eq)]
215pub enum SecureRole {
216    /// Draws the send pad from the first directional half (`i2r`).
217    Initiator,
218    /// Draws the send pad from the second directional half (`r2i`).
219    Responder,
220}
221
222/// The `with pad "<path>" as <role>` binding on a `Connect`/`Listen` — activate the PNP one-time-pad
223/// session over the pad file at `pad`, with this peer taking `role`.
224#[derive(Debug, Clone, Copy)]
225pub struct SecurePad<'a> {
226    /// The pad file path (a string-literal or variable expression).
227    pub pad: &'a Expr<'a>,
228    /// Which directional half this peer sends on.
229    pub role: SecureRole,
230}
231
232#[derive(Debug, Clone)]
233pub enum Stmt<'a> {
234    /// Variable binding: `Let x be 5.` or `Let x: Int be 5.`
235    Let {
236        var: Symbol,
237        ty: Option<&'a TypeExpr<'a>>,
238        value: &'a Expr<'a>,
239        mutable: bool,
240    },
241
242    /// Mutation: `Set x to 10.`
243    Set {
244        target: Symbol,
245        value: &'a Expr<'a>,
246    },
247
248    /// Function call as statement: `Call process with data.`
249    Call {
250        function: Symbol,
251        args: Vec<&'a Expr<'a>>,
252    },
253
254    /// Conditional: `If condition: ... Otherwise: ...`
255    If {
256        cond: &'a Expr<'a>,
257        then_block: Block<'a>,
258        else_block: Option<Block<'a>>,
259    },
260
261    /// Loop: `While condition: ...` or `While condition (decreasing expr): ...`
262    While {
263        cond: &'a Expr<'a>,
264        body: Block<'a>,
265        /// Optional decreasing variant for termination proof.
266        decreasing: Option<&'a Expr<'a>>,
267    },
268
269    /// Iteration: `Repeat for x in list: ...` or `Repeat for i from 1 to 10: ...`
270    Repeat {
271        pattern: Pattern,  // Changed from `var: Symbol` to support tuple destructuring
272        iterable: &'a Expr<'a>,
273        body: Block<'a>,
274    },
275
276    /// Return: `Return x.` or `Return.`
277    Return {
278        value: Option<&'a Expr<'a>>,
279    },
280
281    /// Break: `Break.` — exits the innermost while loop.
282    Break,
283
284    /// Bridge to Logic Kernel: `Assert that P.`
285    Assert {
286        proposition: &'a LogicExpr<'a>,
287    },
288
289    /// Documented assertion with justification.
290    /// `Trust that P because "reason".`
291    /// Semantics: Documented runtime check that could be verified statically.
292    Trust {
293        proposition: &'a LogicExpr<'a>,
294        justification: Symbol,
295    },
296
297    /// Runtime assertion with imperative condition.
298    /// `Assert that condition.` (`hard: false` → `debug_assert!`, a development check)
299    /// `Require that condition.` (`hard: true` → `assert!`, an enforced invariant that
300    /// survives release — the form a proven property lowers to).
301    RuntimeAssert {
302        condition: &'a Expr<'a>,
303        hard: bool,
304    },
305
306    /// Ownership transfer (move): `Give x to processor.`
307    /// Semantics: Move ownership of `object` to `recipient`.
308    Give {
309        object: &'a Expr<'a>,
310        recipient: &'a Expr<'a>,
311    },
312
313    /// Immutable borrow: `Show x to console.`
314    /// Semantics: Immutable borrow of `object` passed to `recipient`.
315    Show {
316        object: &'a Expr<'a>,
317        recipient: &'a Expr<'a>,
318    },
319
320    /// Field mutation: `Set p's x to 10.`
321    SetField {
322        object: &'a Expr<'a>,
323        field: Symbol,
324        value: &'a Expr<'a>,
325    },
326
327    /// Struct definition for codegen.
328    StructDef {
329        name: Symbol,
330        fields: Vec<(Symbol, Symbol, bool)>, // (name, type_name, is_public)
331        is_portable: bool,                    // Derives Serialize/Deserialize
332    },
333
334    /// Function definition.
335    FunctionDef {
336        name: Symbol,
337        /// Generic type parameters: empty for monomorphic functions, e.g. `[T, U]` for polymorphic.
338        generics: Vec<Symbol>,
339        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
340        body: Block<'a>,
341        return_type: Option<&'a TypeExpr<'a>>,
342        is_native: bool,
343        /// Rust path for user-defined native functions (e.g., "reqwest::blocking::get").
344        /// None for system native functions (read, write, etc.) which use map_native_function().
345        native_path: Option<Symbol>,
346        /// Whether this function is exported for FFI (C ABI or WASM).
347        is_exported: bool,
348        /// Export target: None = C ABI (#\[no_mangle\] extern "C"), Some("wasm") = #\[wasm_bindgen\].
349        export_target: Option<Symbol>,
350        /// Per-function optimization config: each `## No <X>` annotation clears
351        /// that optimization's bit (default: all enabled).
352        opt_flags: crate::optimization::OptimizationConfig,
353    },
354
355    /// Pattern matching on sum types.
356    Inspect {
357        target: &'a Expr<'a>,
358        arms: Vec<MatchArm<'a>>,
359        has_otherwise: bool,            // For exhaustiveness tracking
360    },
361
362    /// Push to collection: `Push x to items.`
363    Push {
364        value: &'a Expr<'a>,
365        collection: &'a Expr<'a>,
366    },
367
368    /// Pop from collection: `Pop from items.` or `Pop from items into y.`
369    Pop {
370        collection: &'a Expr<'a>,
371        into: Option<Symbol>,
372    },
373
374    /// Add to set: `Add x to set.`
375    Add {
376        value: &'a Expr<'a>,
377        collection: &'a Expr<'a>,
378    },
379
380    /// Remove from set: `Remove x from set.`
381    Remove {
382        value: &'a Expr<'a>,
383        collection: &'a Expr<'a>,
384    },
385
386    /// Index assignment: `Set item N of X to Y.`
387    SetIndex {
388        collection: &'a Expr<'a>,
389        index: &'a Expr<'a>,
390        value: &'a Expr<'a>,
391    },
392
393    /// A SCOPE-TRANSPARENT statement sequence — parser-desugar output, never
394    /// written by users. One surface statement can lower to several primitive
395    /// statements (a nested place-write `Set item j of (item i of grid) to v`
396    /// becomes read → copy-on-write element write → write-back; a multi-push
397    /// `Push a, b, c to xs` becomes one Push per element). The body runs in
398    /// the ENCLOSING scope with no block of its own: temporaries inside are
399    /// gensym'd (`__place_*`), so they can never collide with user names, and
400    /// engines that execute blocks with scoping must NOT scope this one.
401    Splice {
402        body: Block<'a>,
403    },
404
405    /// Memory arena block (Zone).
406    /// "Inside a new zone called 'Scratch':"
407    /// "Inside a zone called 'Buffer' of size 1 MB:"
408    /// "Inside a zone called 'Data' mapped from 'file.bin':"
409    Zone {
410        /// The variable name for the arena handle (e.g., "Scratch")
411        name: Symbol,
412        /// Optional pre-allocated capacity in bytes (Heap zones only)
413        capacity: Option<usize>,
414        /// Optional backing file for memory-mapped zones (literal path or runtime variable)
415        source_file: Option<ZoneSource>,
416        /// The code block executed within this memory context
417        body: Block<'a>,
418    },
419
420    /// Concurrent execution block (async, I/O-bound).
421    /// "Attempt all of the following:"
422    /// Semantics: All tasks run concurrently via tokio::join!
423    /// Best for: network requests, file I/O, waiting operations
424    Concurrent {
425        /// The statements to execute concurrently
426        tasks: Block<'a>,
427    },
428
429    /// Parallel execution block (CPU-bound).
430    /// "Simultaneously:"
431    /// Semantics: True parallelism via rayon::join or thread::spawn
432    /// Best for: computation, data processing, number crunching
433    Parallel {
434        /// The statements to execute in parallel
435        tasks: Block<'a>,
436    },
437
438    /// Read from console or file.
439    /// `Read input from the console.` or `Read data from file "path.txt".`
440    ReadFrom {
441        var: Symbol,
442        source: ReadSource<'a>,
443    },
444
445    /// Write to file.
446    /// `Write "content" to file "output.txt".`
447    WriteFile {
448        content: &'a Expr<'a>,
449        path: &'a Expr<'a>,
450    },
451
452    /// Spawn an agent.
453    /// `Spawn a Worker called "w1".`
454    Spawn {
455        agent_type: Symbol,
456        name: Symbol,
457    },
458
459    /// Send message to agent.
460    /// `Send Ping to "agent".`, `Send compressed Ping to "agent".`,
461    /// `Send cached Point to "agent".`, or `Send cached compressed Report to "agent".`
462    SendMessage {
463        message: &'a Expr<'a>,
464        destination: &'a Expr<'a>,
465        /// The wire compression codec. `None` for a plain `Send`; `Some(codec)` for
466        /// `Send compressed [with <codec>]` (bare `compressed` = deflate). Kept only
467        /// if it actually shrinks the body.
468        compression: Option<CompressionCodec>,
469        /// `Send cached …` — use the connection's schema dictionary, so a struct
470        /// schema is transmitted once and referenced thereafter (content-addressed,
471        /// footgun-free). `false` for a plain `Send`.
472        cached: bool,
473        /// `Send unchecked …` — drop the wire integrity checksum for the fastest path
474        /// (latency↔safety dial). `false` keeps the default checksum.
475        unchecked: bool,
476        /// `Send fast|compact|packed …` — the wire LAYOUT (size↔speed dial). `None` is
477        /// the default (compact / varint). The sender picks for their link.
478        layout: Option<SendLayout>,
479        /// `Send shared …` — OPT-IN type-id elision: drop struct/enum NAMES off the wire
480        /// (ship a small registry id both ends derive from their shared program type
481        /// defs). Only safe when the receiver runs the same program, so it is OFF by
482        /// default — the default `Send` stays self-describing for any peer / relay.
483        shared: bool,
484        /// `Send computed f …` — COMPUTE-SHIPPING: when the message is a pure single-arg
485        /// function, lower it to a sandboxed generator and ship the COMPUTATION, not data.
486        /// The receiver evaluates it in the bounded sandbox (never arbitrary code). OFF by
487        /// default; a non-lowerable function under `computed` is rejected at send.
488        computed: bool,
489        /// `Send indexed …` (alias `addressable`) — encode a record list in the random-access
490        /// struct-view LAYOUT (row + field offset tables), so the receiver reaches any
491        /// (row, field) in O(1) without decoding the rest — Cap'n Proto's home turf. Composes
492        /// with the other knobs (`compressed`, `cached`, `shared`, `unchecked`). OFF by default
493        /// (the dense columnar form is smaller); opt in when the peer does random field reads.
494        indexed: bool,
495        /// `Send deduped …` — Rc-DEDUP: a subtree the same value reaches more than once ships ONCE
496        /// (the first occurrence) plus a tiny backref for every repeat, and the receiver rebuilds the
497        /// SHARING (one aliased value, not N copies). OFF by default; opt in when the message has
498        /// shared sub-structure (a lookup table referenced by many records, one object aliased
499        /// across the payload). Self-describing by tag, so any peer decodes it.
500        deduped: bool,
501    },
502
503    /// Await response from agent.
504    /// `Await response from "agent" into result.`
505    AwaitMessage {
506        source: &'a Expr<'a>,
507        into: Symbol,
508        /// `Await view from …`: hold a received record-list LAZILY (the zero-copy receive —
509        /// decode-on-touch, no rows materialized until a field is read) instead of fully decoding
510        /// it. Ignored for non-record-list shapes, which always decode eagerly.
511        view: bool,
512        /// `Await stream from …`: receive a batch STREAM message and deframe it into a list (the
513        /// values a peer `Stream`ed), rather than a single message.
514        stream: bool,
515    },
516
517    /// Stream a batch of values to a peer in one framed message.
518    /// `Stream readings to "sink".` — frames each element of `values` length-delimited so the
519    /// receiver (`Await stream from …`) deframes them incrementally; one relay publish ships the
520    /// whole batch (Kafka-style streaming that amortizes per-message overhead).
521    StreamMessage {
522        values: &'a Expr<'a>,
523        destination: &'a Expr<'a>,
524    },
525
526    /// Merge CRDT state.
527    /// `Merge remote into local.` or `Merge remote's field into local's field.`
528    MergeCrdt {
529        source: &'a Expr<'a>,
530        target: &'a Expr<'a>,
531    },
532
533    /// Increment GCounter.
534    /// `Increase local's points by 10.`
535    IncreaseCrdt {
536        object: &'a Expr<'a>,
537        field: Symbol,
538        amount: &'a Expr<'a>,
539    },
540
541    /// Decrement PNCounter (Tally).
542    /// `Decrease game's score by 5.`
543    DecreaseCrdt {
544        object: &'a Expr<'a>,
545        field: Symbol,
546        amount: &'a Expr<'a>,
547    },
548
549    /// Append to SharedSequence (RGA).
550    /// `Append "Hello" to doc's lines.`
551    AppendToSequence {
552        sequence: &'a Expr<'a>,
553        value: &'a Expr<'a>,
554    },
555
556    /// Resolve MVRegister conflicts.
557    /// `Resolve page's title to "Final".`
558    ResolveConflict {
559        object: &'a Expr<'a>,
560        field: Symbol,
561        value: &'a Expr<'a>,
562    },
563
564    /// Security check - mandatory runtime guard.
565    /// `Check that user is admin.`
566    /// `Check that user can publish the document.`
567    /// Semantics: NEVER optimized out. Panics if condition is false.
568    Check {
569        /// The subject being checked (e.g., "user")
570        subject: Symbol,
571        /// The predicate name (e.g., "admin") or action (e.g., "publish")
572        predicate: Symbol,
573        /// True if this is a capability check (`can [action]`)
574        is_capability: bool,
575        /// For capabilities: the object being acted on (e.g., "document")
576        object: Option<Symbol>,
577        /// Original English text for error message
578        source_text: String,
579        /// Source location for error reporting
580        span: crate::token::Span,
581    },
582
583    /// Listen on network address.
584    /// `Listen on "/ip4/127.0.0.1/tcp/8000".`
585    /// Semantics: Bind to address, start accepting connections via libp2p
586    Listen {
587        address: &'a Expr<'a>,
588        /// Optional PNP one-time-pad binding: `with pad "<path>" as initiator|responder`.
589        secure: Option<SecurePad<'a>>,
590    },
591
592    /// Connect to remote peer.
593    /// `Connect to "/ip4/127.0.0.1/tcp/8000".`
594    /// Semantics: Dial peer via libp2p
595    ConnectTo {
596        address: &'a Expr<'a>,
597        /// Optional PNP one-time-pad binding: `with pad "<path>" as initiator|responder`.
598        secure: Option<SecurePad<'a>>,
599    },
600
601    /// Create PeerAgent remote handle.
602    /// `Let remote be a PeerAgent at "/ip4/127.0.0.1/tcp/8000".`
603    /// Semantics: Create handle for remote agent communication
604    LetPeerAgent {
605        var: Symbol,
606        address: &'a Expr<'a>,
607    },
608
609    /// Sleep for milliseconds.
610    /// `Sleep 1000.` or `Sleep delay.`
611    /// Semantics: Pause execution for N milliseconds (async)
612    Sleep {
613        milliseconds: &'a Expr<'a>,
614    },
615
616    /// Sync CRDT variable on topic.
617    /// `Sync x on "topic".`
618    /// Semantics: Subscribe to GossipSub topic, auto-publish on mutation, auto-merge on receive
619    Sync {
620        var: Symbol,
621        topic: &'a Expr<'a>,
622    },
623
624    /// Mount persistent CRDT from journal file.
625    /// `Mount counter at "data/counter.journal".`
626    /// Semantics: Load or create journal, replay operations to reconstruct state
627    Mount {
628        /// The variable name for the mounted value
629        var: Symbol,
630        /// The path expression for the journal file
631        path: &'a Expr<'a>,
632    },
633
634    // =========================================================================
635    // Go-like Concurrency (Green Threads, Channels, Select)
636    // =========================================================================
637
638    /// Launch a fire-and-forget task (green thread).
639    /// `Launch a task to process(data).`
640    /// Semantics: tokio::spawn with no handle capture
641    LaunchTask {
642        /// The function to call
643        function: Symbol,
644        /// Arguments to pass
645        args: Vec<&'a Expr<'a>>,
646    },
647
648    /// Launch a task with handle for control.
649    /// `Let worker be Launch a task to process(data).`
650    /// Semantics: tokio::spawn returning JoinHandle
651    LaunchTaskWithHandle {
652        /// Variable to bind the handle
653        handle: Symbol,
654        /// The function to call
655        function: Symbol,
656        /// Arguments to pass
657        args: Vec<&'a Expr<'a>>,
658    },
659
660    /// Create a bounded channel (pipe).
661    /// `Let jobs be a new Pipe of Int.`
662    /// Semantics: tokio::sync::mpsc::channel(32)
663    CreatePipe {
664        /// Variable for the pipe
665        var: Symbol,
666        /// Type of values in the pipe
667        element_type: Symbol,
668        /// Optional capacity (defaults to 32)
669        capacity: Option<u32>,
670    },
671
672    /// Blocking send into pipe.
673    /// `Send value into pipe.`
674    /// Semantics: pipe_tx.send(value).await
675    SendPipe {
676        /// The value to send
677        value: &'a Expr<'a>,
678        /// The pipe to send into
679        pipe: &'a Expr<'a>,
680    },
681
682    /// Blocking receive from pipe.
683    /// `Receive x from pipe.`
684    /// Semantics: let x = pipe_rx.recv().await
685    ReceivePipe {
686        /// Variable to bind the received value
687        var: Symbol,
688        /// The pipe to receive from
689        pipe: &'a Expr<'a>,
690    },
691
692    /// Non-blocking send (try).
693    /// `Try to send value into pipe.`
694    /// Semantics: pipe_tx.try_send(value) - returns immediately
695    TrySendPipe {
696        /// The value to send
697        value: &'a Expr<'a>,
698        /// The pipe to send into
699        pipe: &'a Expr<'a>,
700        /// Variable to bind the result (true/false)
701        result: Option<Symbol>,
702    },
703
704    /// Non-blocking receive (try).
705    /// `Try to receive x from pipe.`
706    /// Semantics: pipe_rx.try_recv() - returns Option
707    TryReceivePipe {
708        /// Variable to bind the received value (if any)
709        var: Symbol,
710        /// The pipe to receive from
711        pipe: &'a Expr<'a>,
712    },
713
714    /// Cancel a spawned task.
715    /// `Stop worker.`
716    /// Semantics: handle.abort()
717    StopTask {
718        /// The handle to cancel
719        handle: &'a Expr<'a>,
720    },
721
722    /// Select on multiple channels/timeouts.
723    /// `Await the first of:`
724    ///     `Receive x from ch:`
725    ///         `...`
726    ///     `After 5 seconds:`
727    ///         `...`
728    /// Semantics: tokio::select! with auto-cancel
729    Select {
730        /// The branches to select from
731        branches: Vec<SelectBranch<'a>>,
732    },
733
734    /// Theorem block.
735    /// `## Theorem: Name`
736    /// `Given: Premise.`
737    /// `Prove: Goal.`
738    /// `Proof: Auto.`
739    Theorem(TheoremBlock<'a>),
740
741    /// `## Define` block — a vernacular-logic predicate definition (Rung 0a).
742    /// `x is a bachelor if and only if x is unmarried and x is a man.`
743    /// Non-executable: like [`Stmt::Theorem`], it is a declaration the proof
744    /// layer consumes, not code the VM/AOT runs.
745    Definition(DefinitionBlock<'a>),
746
747    /// `## Axiom` block — a named first-order axiom in formal notation. Registers a
748    /// shared premise for later theorems (the seam for an axiomatic base like Tarski).
749    Axiom(AxiomBlock),
750
751    /// `## Theory` block — a named development grouping formal axioms and theorems.
752    Theory(TheoryBlock),
753
754    /// Escape hatch: embed raw foreign code.
755    /// `Escape to Rust:` followed by an indented block of raw code.
756    ///
757    /// Variables from the enclosing LOGOS scope are available in the
758    /// escape block as their generated Rust types. The raw code is
759    /// emitted verbatim inside a `{ ... }` block in the generated Rust.
760    Escape {
761        /// Target language ("Rust" for now, forward-compatible with "Python", "WGSL", etc.)
762        language: Symbol,
763        /// Raw foreign code, captured verbatim with base indentation stripped.
764        code: Symbol,
765        /// Source span covering the entire escape block (header + body).
766        span: crate::token::Span,
767    },
768
769    /// Dependency declaration from `## Requires` block.
770    /// The "serde" crate version "1.0" with features "derive".
771    Require {
772        crate_name: Symbol,
773        version: Symbol,
774        features: Vec<Symbol>,
775        span: crate::token::Span,
776    },
777}
778
779/// A branch in a Select statement.
780#[derive(Debug, Clone)]
781pub enum SelectBranch<'a> {
782    /// Receive from a pipe: `Receive x from ch:`
783    Receive {
784        var: Symbol,
785        pipe: &'a Expr<'a>,
786        body: Block<'a>,
787    },
788    /// Timeout: `After N seconds:` or `After N milliseconds:`
789    Timeout {
790        milliseconds: &'a Expr<'a>,
791        body: Block<'a>,
792    },
793}
794
795/// Shared expression type for pure computations (LOGOS §15.0.0).
796///
797/// Expr is used by both LogicExpr (as terms) and Stmt (as values).
798/// These are pure computations without side effects.
799#[derive(Debug)]
800pub enum Expr<'a> {
801    /// Literal value: 42, "hello", true, nothing
802    Literal(Literal),
803
804    /// Variable reference: x
805    Identifier(Symbol),
806
807    /// Binary operation: x plus y
808    BinaryOp {
809        op: BinaryOpKind,
810        left: &'a Expr<'a>,
811        right: &'a Expr<'a>,
812    },
813
814    /// Unary NOT: "not x" → `!x` (logical for Bool, bitwise for Int)
815    Not {
816        operand: &'a Expr<'a>,
817    },
818
819    /// Function call as expression: f(x, y)
820    Call {
821        function: Symbol,
822        args: Vec<&'a Expr<'a>>,
823    },
824
825    /// Dynamic index access: `items at i` (1-indexed).
826    Index {
827        collection: &'a Expr<'a>,
828        index: &'a Expr<'a>,
829    },
830
831    /// Dynamic slice access: `items 1 through mid` (1-indexed, inclusive).
832    Slice {
833        collection: &'a Expr<'a>,
834        start: &'a Expr<'a>,
835        end: &'a Expr<'a>,
836    },
837
838    /// Copy expression: `copy of slice` → slice.to_vec().
839    Copy {
840        expr: &'a Expr<'a>,
841    },
842
843    /// Give expression: `Give x` → transfers ownership, no clone needed.
844    /// Used in function calls to explicitly move values.
845    Give {
846        value: &'a Expr<'a>,
847    },
848
849    /// Length expression: `length of items` → items.len().
850    Length {
851        collection: &'a Expr<'a>,
852    },
853
854    /// Set contains: `set contains x` or `x in set`
855    Contains {
856        collection: &'a Expr<'a>,
857        value: &'a Expr<'a>,
858    },
859
860    /// Set union: `a union b`
861    Union {
862        left: &'a Expr<'a>,
863        right: &'a Expr<'a>,
864    },
865
866    /// Set intersection: `a intersection b`
867    Intersection {
868        left: &'a Expr<'a>,
869        right: &'a Expr<'a>,
870    },
871
872    /// Get manifest of a zone.
873    /// `the manifest of Zone` → FileSipper::from_zone(&zone).manifest()
874    ManifestOf {
875        zone: &'a Expr<'a>,
876    },
877
878    /// Get chunk at index from a zone.
879    /// `the chunk at N in Zone` → FileSipper::from_zone(&zone).get_chunk(N)
880    ChunkAt {
881        index: &'a Expr<'a>,
882        zone: &'a Expr<'a>,
883    },
884
885    /// List literal: [1, 2, 3]
886    List(Vec<&'a Expr<'a>>),
887
888    /// Tuple literal: (1, "hello", true)
889    Tuple(Vec<&'a Expr<'a>>),
890
891    /// Range: 1 to 10 (inclusive)
892    Range {
893        start: &'a Expr<'a>,
894        end: &'a Expr<'a>,
895    },
896
897    /// Field access: `p's x` or `the x of p`.
898    FieldAccess {
899        object: &'a Expr<'a>,
900        field: Symbol,
901    },
902
903    /// Constructor: `a new Point` or `a new Point with x 10 and y 20`.
904    /// Supports generics: `a new Box of Int` and nested types: `a new Seq of (Seq of Int)`
905    New {
906        type_name: Symbol,
907        type_args: Vec<TypeExpr<'a>>,  // Empty for non-generic types - now supports nested types
908        init_fields: Vec<(Symbol, &'a Expr<'a>)>,  // Optional field initialization
909    },
910
911    /// Enum variant constructor: `a new Circle with radius 10`.
912    NewVariant {
913        enum_name: Symbol,                      // Shape (resolved from registry)
914        variant: Symbol,                        // Circle
915        fields: Vec<(Symbol, &'a Expr<'a>)>,    // [(radius, 10)]
916    },
917
918    /// Escape hatch expression: raw foreign code that produces a value.
919    /// Used in expression position: `Let x: Int be Escape to Rust:`
920    Escape {
921        language: Symbol,
922        code: Symbol,
923    },
924
925    /// Option Some: `some 30` → Some(30)
926    OptionSome {
927        value: &'a Expr<'a>,
928    },
929
930    /// Option None: `none` → None
931    OptionNone,
932
933    /// Pre-allocation capacity hint wrapping an inner value expression.
934    /// `"" with capacity 100` or `a new Seq of Int with capacity n`
935    /// Codegen uses with_capacity(); interpreter ignores the hint.
936    WithCapacity {
937        value: &'a Expr<'a>,
938        capacity: &'a Expr<'a>,
939    },
940
941    /// Closure expression: `(params) -> body` or `(params) ->:` block body.
942    /// Captures variables from the enclosing scope by value (snapshot/clone).
943    Closure {
944        params: Vec<(Symbol, &'a TypeExpr<'a>)>,
945        body: ClosureBody<'a>,
946        return_type: Option<&'a TypeExpr<'a>>,
947    },
948
949    /// Call an expression that evaluates to a callable value.
950    /// `f(x)` where `f` is a variable holding a closure, not a named function.
951    CallExpr {
952        callee: &'a Expr<'a>,
953        args: Vec<&'a Expr<'a>>,
954    },
955
956    /// Interpolated string: `"Hello, {name}! Value: {x:.2}"`
957    InterpolatedString(Vec<StringPart<'a>>),
958}
959
960/// A segment of an interpolated string.
961#[derive(Debug, Clone)]
962pub enum StringPart<'a> {
963    /// Literal text segment
964    Literal(Symbol),
965    /// Expression with optional format specifier
966    Expr {
967        value: &'a Expr<'a>,
968        format_spec: Option<Symbol>,
969        /// Self-documenting debug format: `{var=}` → `"var=42"`
970        debug: bool,
971    },
972}
973
974/// Body of a closure expression.
975#[derive(Debug, Clone)]
976pub enum ClosureBody<'a> {
977    /// Single expression: `(n: Int) -> n * 2`
978    Expression(&'a Expr<'a>),
979    /// Block of statements: `(n: Int) ->:` followed by indented body
980    Block(Block<'a>),
981}
982
983/// Literal values in LOGOS.
984#[derive(Debug, Clone)]
985pub enum Literal {
986    /// Integer literal
987    Number(i64),
988    /// Float literal
989    Float(f64),
990    /// Text literal
991    Text(Symbol),
992    /// Boolean literal
993    Boolean(bool),
994    /// The nothing literal (unit type)
995    Nothing,
996    /// Character literal
997    Char(char),
998    /// Duration literal (nanoseconds, signed for negative offsets like "5 minutes early")
999    Duration(i64),
1000    /// Date literal (days since Unix epoch 1970-01-01)
1001    Date(i32),
1002    /// Moment literal (nanoseconds since Unix epoch)
1003    Moment(i64),
1004    /// Calendar span (months, days) - NOT flattened to seconds
1005    /// Months and days are kept separate because they're incommensurable.
1006    Span { months: i32, days: i32 },
1007    /// Time-of-day literal (nanoseconds from midnight)
1008    /// Range: 0 to 86_399_999_999_999 (just under 24 hours)
1009    Time(i64),
1010}
1011
1012impl PartialEq for Literal {
1013    fn eq(&self, other: &Self) -> bool {
1014        match (self, other) {
1015            (Literal::Number(a), Literal::Number(b)) => a == b,
1016            (Literal::Float(a), Literal::Float(b)) => a.to_bits() == b.to_bits(),
1017            (Literal::Text(a), Literal::Text(b)) => a == b,
1018            (Literal::Boolean(a), Literal::Boolean(b)) => a == b,
1019            (Literal::Nothing, Literal::Nothing) => true,
1020            (Literal::Char(a), Literal::Char(b)) => a == b,
1021            (Literal::Duration(a), Literal::Duration(b)) => a == b,
1022            (Literal::Date(a), Literal::Date(b)) => a == b,
1023            (Literal::Moment(a), Literal::Moment(b)) => a == b,
1024            (Literal::Span { months: m1, days: d1 }, Literal::Span { months: m2, days: d2 }) => m1 == m2 && d1 == d2,
1025            (Literal::Time(a), Literal::Time(b)) => a == b,
1026            _ => false,
1027        }
1028    }
1029}
1030
1031impl Eq for Literal {}
1032
1033impl std::hash::Hash for Literal {
1034    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1035        std::mem::discriminant(self).hash(state);
1036        match self {
1037            Literal::Number(n) => n.hash(state),
1038            Literal::Float(f) => f.to_bits().hash(state),
1039            Literal::Text(s) => s.hash(state),
1040            Literal::Boolean(b) => b.hash(state),
1041            Literal::Nothing => {}
1042            Literal::Char(c) => c.hash(state),
1043            Literal::Duration(d) => d.hash(state),
1044            Literal::Date(d) => d.hash(state),
1045            Literal::Moment(m) => m.hash(state),
1046            Literal::Span { months, days } => {
1047                months.hash(state);
1048                days.hash(state);
1049            }
1050            Literal::Time(t) => t.hash(state),
1051        }
1052    }
1053}