sigil_parser/
ir.rs

1//! AI-Facing Intermediate Representation (IR)
2//!
3//! This module provides a JSON-serializable IR designed for consumption by
4//! AI agents, language servers, and external tooling. The IR captures:
5//!
6//! - Function definitions with typed parameters and bodies
7//! - Pipeline operations (morphemes, transformations, forks)
8//! - Evidentiality annotations throughout
9//! - Type information including the evidentiality lattice
10//! - Control flow and data flow structures
11//! - Protocol operations with their trust boundaries
12
13use serde::{Deserialize, Serialize};
14
15/// The version of the IR schema
16pub const IR_VERSION: &str = "1.0.0";
17
18/// Top-level IR module representing a complete Sigil program
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct IrModule {
21    /// Schema version
22    pub version: String,
23    /// Source file path
24    pub source: String,
25    /// All function definitions
26    pub functions: Vec<IrFunction>,
27    /// Type definitions (structs, enums)
28    pub types: Vec<IrTypeDef>,
29    /// Trait definitions
30    pub traits: Vec<IrTraitDef>,
31    /// Impl blocks
32    pub impls: Vec<IrImplBlock>,
33    /// Constants
34    pub constants: Vec<IrConstant>,
35    /// The evidentiality lattice structure
36    pub evidentiality_lattice: EvidentialityLattice,
37}
38
39impl IrModule {
40    /// Create a new empty IR module
41    pub fn new(source: String) -> Self {
42        Self {
43            version: IR_VERSION.to_string(),
44            source,
45            functions: Vec::new(),
46            types: Vec::new(),
47            traits: Vec::new(),
48            impls: Vec::new(),
49            constants: Vec::new(),
50            evidentiality_lattice: EvidentialityLattice::default(),
51        }
52    }
53}
54
55/// The evidentiality lattice with explicit join/meet rules
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct EvidentialityLattice {
58    /// The four evidence levels in order
59    pub levels: Vec<EvidenceLevel>,
60    /// Join rules (pessimistic combination)
61    pub join_rules: Vec<LatticeRule>,
62    /// Meet rules (optimistic combination)
63    pub meet_rules: Vec<LatticeRule>,
64}
65
66impl Default for EvidentialityLattice {
67    fn default() -> Self {
68        Self {
69            levels: vec![
70                EvidenceLevel {
71                    name: "known".to_string(),
72                    symbol: "!".to_string(),
73                    order: 0,
74                    description: "Direct computation, verified".to_string(),
75                },
76                EvidenceLevel {
77                    name: "uncertain".to_string(),
78                    symbol: "?".to_string(),
79                    order: 1,
80                    description: "Inferred, possibly absent".to_string(),
81                },
82                EvidenceLevel {
83                    name: "reported".to_string(),
84                    symbol: "~".to_string(),
85                    order: 2,
86                    description: "External source, untrusted".to_string(),
87                },
88                EvidenceLevel {
89                    name: "paradox".to_string(),
90                    symbol: "‽".to_string(),
91                    order: 3,
92                    description: "Contradictory, trust boundary".to_string(),
93                },
94            ],
95            join_rules: vec![
96                LatticeRule::new("known", "uncertain", "uncertain"),
97                LatticeRule::new("known", "reported", "reported"),
98                LatticeRule::new("known", "paradox", "paradox"),
99                LatticeRule::new("uncertain", "reported", "reported"),
100                LatticeRule::new("uncertain", "paradox", "paradox"),
101                LatticeRule::new("reported", "paradox", "paradox"),
102            ],
103            meet_rules: vec![
104                LatticeRule::new("known", "uncertain", "known"),
105                LatticeRule::new("known", "reported", "known"),
106                LatticeRule::new("known", "paradox", "known"),
107                LatticeRule::new("uncertain", "reported", "uncertain"),
108                LatticeRule::new("uncertain", "paradox", "uncertain"),
109                LatticeRule::new("reported", "paradox", "reported"),
110            ],
111        }
112    }
113}
114
115/// An evidence level in the lattice
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct EvidenceLevel {
118    pub name: String,
119    pub symbol: String,
120    pub order: u8,
121    pub description: String,
122}
123
124/// A join/meet rule in the lattice
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct LatticeRule {
127    pub left: String,
128    pub right: String,
129    pub result: String,
130}
131
132impl LatticeRule {
133    pub fn new(left: &str, right: &str, result: &str) -> Self {
134        Self {
135            left: left.to_string(),
136            right: right.to_string(),
137            result: result.to_string(),
138        }
139    }
140}
141
142/// Evidence marker enum matching AST Evidentiality
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "lowercase")]
145pub enum IrEvidence {
146    Known,
147    Uncertain,
148    Reported,
149    Paradox,
150}
151
152impl IrEvidence {
153    /// Join two evidence levels (pessimistic - takes the higher/less certain)
154    pub fn join(self, other: IrEvidence) -> IrEvidence {
155        match (self, other) {
156            (IrEvidence::Paradox, _) | (_, IrEvidence::Paradox) => IrEvidence::Paradox,
157            (IrEvidence::Reported, _) | (_, IrEvidence::Reported) => IrEvidence::Reported,
158            (IrEvidence::Uncertain, _) | (_, IrEvidence::Uncertain) => IrEvidence::Uncertain,
159            (IrEvidence::Known, IrEvidence::Known) => IrEvidence::Known,
160        }
161    }
162
163    /// Meet two evidence levels (optimistic - takes the lower/more certain)
164    pub fn meet(self, other: IrEvidence) -> IrEvidence {
165        match (self, other) {
166            (IrEvidence::Known, _) | (_, IrEvidence::Known) => IrEvidence::Known,
167            (IrEvidence::Uncertain, _) | (_, IrEvidence::Uncertain) => IrEvidence::Uncertain,
168            (IrEvidence::Reported, _) | (_, IrEvidence::Reported) => IrEvidence::Reported,
169            (IrEvidence::Paradox, IrEvidence::Paradox) => IrEvidence::Paradox,
170        }
171    }
172
173    /// Get the symbol for this evidence level
174    pub fn symbol(&self) -> &'static str {
175        match self {
176            IrEvidence::Known => "!",
177            IrEvidence::Uncertain => "?",
178            IrEvidence::Reported => "~",
179            IrEvidence::Paradox => "‽",
180        }
181    }
182}
183
184impl Default for IrEvidence {
185    fn default() -> Self {
186        IrEvidence::Known
187    }
188}
189
190/// Source location span
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct IrSpan {
193    pub start: IrPosition,
194    pub end: IrPosition,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct IrPosition {
199    pub line: usize,
200    pub column: usize,
201}
202
203/// Function visibility
204#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
205#[serde(rename_all = "lowercase")]
206pub enum IrVisibility {
207    Public,
208    Private,
209    Crate,
210}
211
212impl Default for IrVisibility {
213    fn default() -> Self {
214        IrVisibility::Private
215    }
216}
217
218/// A function definition
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct IrFunction {
221    /// Function name
222    pub name: String,
223    /// Unique identifier
224    pub id: String,
225    /// Visibility
226    pub visibility: IrVisibility,
227    /// Generic type parameters
228    pub generics: Vec<IrGenericParam>,
229    /// Parameters
230    pub params: Vec<IrParam>,
231    /// Return type
232    pub return_type: IrType,
233    /// Function body
234    pub body: Option<IrOperation>,
235    /// Attributes (inline, pure, async, etc.)
236    pub attributes: Vec<String>,
237    /// Whether this is an async function
238    pub is_async: bool,
239    /// Source span
240    #[serde(skip_serializing_if = "Option::is_none")]
241    pub span: Option<IrSpan>,
242}
243
244/// A generic type parameter
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct IrGenericParam {
247    pub name: String,
248    pub bounds: Vec<String>,
249}
250
251/// A function parameter
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct IrParam {
254    pub name: String,
255    #[serde(rename = "type")]
256    pub ty: IrType,
257    pub evidence: IrEvidence,
258}
259
260/// Type representation in IR
261#[derive(Debug, Clone, Serialize, Deserialize)]
262#[serde(tag = "kind")]
263pub enum IrType {
264    /// Primitive types (i32, f64, bool, str, etc.)
265    #[serde(rename = "primitive")]
266    Primitive { name: String },
267
268    /// Unit type ()
269    #[serde(rename = "unit")]
270    Unit,
271
272    /// Never type !
273    #[serde(rename = "never")]
274    Never,
275
276    /// Array type [T; N]
277    #[serde(rename = "array")]
278    Array {
279        element: Box<IrType>,
280        size: Option<usize>,
281    },
282
283    /// Slice type [T]
284    #[serde(rename = "slice")]
285    Slice { element: Box<IrType> },
286
287    /// Tuple type (A, B, C)
288    #[serde(rename = "tuple")]
289    Tuple { elements: Vec<IrType> },
290
291    /// Named struct/enum type
292    #[serde(rename = "named")]
293    Named { name: String, generics: Vec<IrType> },
294
295    /// Reference &T, &mut T, &'a T, &'static mut T
296    #[serde(rename = "reference")]
297    Reference {
298        lifetime: Option<String>,
299        mutable: bool,
300        inner: Box<IrType>,
301    },
302
303    /// Pointer *const T or *mut T
304    #[serde(rename = "pointer")]
305    Pointer { mutable: bool, inner: Box<IrType> },
306
307    /// Function type fn(A, B) -> C
308    #[serde(rename = "function")]
309    Function {
310        params: Vec<IrType>,
311        return_type: Box<IrType>,
312        is_async: bool,
313    },
314
315    /// Evidential wrapper E<T> with evidence level
316    #[serde(rename = "evidential")]
317    Evidential {
318        inner: Box<IrType>,
319        evidence: IrEvidence,
320    },
321
322    /// Cyclic arithmetic type Z/nZ
323    #[serde(rename = "cycle")]
324    Cycle { modulus: u64 },
325
326    /// SIMD vector type
327    #[serde(rename = "simd")]
328    Simd { element: Box<IrType>, lanes: usize },
329
330    /// Atomic type
331    #[serde(rename = "atomic")]
332    Atomic { inner: Box<IrType> },
333
334    /// Type variable (for inference)
335    #[serde(rename = "var")]
336    Var { id: String },
337
338    /// Infer placeholder _
339    #[serde(rename = "infer")]
340    Infer,
341
342    /// Lifetime bound 'static, 'a
343    #[serde(rename = "lifetime")]
344    Lifetime { name: String },
345
346    /// Trait object: dyn Trait
347    #[serde(rename = "trait_object")]
348    TraitObject { bounds: Vec<IrType> },
349
350    /// Higher-ranked trait bound: for<'a> Trait<'a>
351    #[serde(rename = "hrtb")]
352    Hrtb {
353        lifetimes: Vec<String>,
354        bound: Box<IrType>,
355    },
356
357    /// Inline struct type: struct { field: Type, ... }
358    #[serde(rename = "inline_struct")]
359    InlineStruct { fields: Vec<(String, IrType)> },
360
361    /// Impl trait: impl Trait bounds
362    #[serde(rename = "impl_trait")]
363    ImplTrait { bounds: Vec<IrType> },
364
365    /// Inline enum type: enum { Variant1, Variant2, ... }
366    #[serde(rename = "inline_enum")]
367    InlineEnum { variants: Vec<String> },
368
369    /// Associated type binding: Output = Type
370    #[serde(rename = "assoc_type_binding")]
371    AssocTypeBinding { name: String, ty: Box<IrType> },
372
373    /// Error recovery type
374    #[serde(rename = "error")]
375    Error,
376}
377
378impl Default for IrType {
379    fn default() -> Self {
380        IrType::Unit
381    }
382}
383
384/// Type definition (struct, enum)
385#[derive(Debug, Clone, Serialize, Deserialize)]
386#[serde(tag = "kind")]
387pub enum IrTypeDef {
388    #[serde(rename = "struct_def")]
389    Struct {
390        name: String,
391        generics: Vec<IrGenericParam>,
392        fields: Vec<IrField>,
393        #[serde(skip_serializing_if = "Option::is_none")]
394        span: Option<IrSpan>,
395    },
396    #[serde(rename = "enum_def")]
397    Enum {
398        name: String,
399        generics: Vec<IrGenericParam>,
400        variants: Vec<IrVariant>,
401        #[serde(skip_serializing_if = "Option::is_none")]
402        span: Option<IrSpan>,
403    },
404    #[serde(rename = "type_alias")]
405    TypeAlias {
406        name: String,
407        generics: Vec<IrGenericParam>,
408        target: IrType,
409        #[serde(skip_serializing_if = "Option::is_none")]
410        span: Option<IrSpan>,
411    },
412}
413
414/// A struct field
415#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct IrField {
417    pub name: String,
418    #[serde(rename = "type")]
419    pub ty: IrType,
420    pub visibility: IrVisibility,
421}
422
423/// An enum variant
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct IrVariant {
426    pub name: String,
427    pub fields: Option<Vec<IrField>>,
428    pub discriminant: Option<i64>,
429}
430
431/// Trait definition
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct IrTraitDef {
434    pub name: String,
435    pub generics: Vec<IrGenericParam>,
436    pub super_traits: Vec<String>,
437    pub methods: Vec<IrFunction>,
438    #[serde(skip_serializing_if = "Option::is_none")]
439    pub span: Option<IrSpan>,
440}
441
442/// Impl block
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct IrImplBlock {
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub trait_name: Option<String>,
447    pub target_type: IrType,
448    pub generics: Vec<IrGenericParam>,
449    pub methods: Vec<IrFunction>,
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub span: Option<IrSpan>,
452}
453
454/// Constant definition
455#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct IrConstant {
457    pub name: String,
458    #[serde(rename = "type")]
459    pub ty: IrType,
460    pub value: IrOperation,
461    pub visibility: IrVisibility,
462    #[serde(skip_serializing_if = "Option::is_none")]
463    pub span: Option<IrSpan>,
464}
465
466/// Core operation node - the heart of the IR
467#[derive(Debug, Clone, Serialize, Deserialize)]
468#[serde(tag = "kind")]
469pub enum IrOperation {
470    // === Literals ===
471    #[serde(rename = "literal")]
472    Literal {
473        variant: LiteralVariant,
474        value: serde_json::Value,
475        #[serde(rename = "type")]
476        ty: IrType,
477        evidence: IrEvidence,
478    },
479
480    // === Variables ===
481    #[serde(rename = "var")]
482    Var {
483        name: String,
484        id: String,
485        #[serde(rename = "type")]
486        ty: IrType,
487        evidence: IrEvidence,
488    },
489
490    // === Bindings ===
491    #[serde(rename = "let")]
492    Let {
493        pattern: IrPattern,
494        #[serde(skip_serializing_if = "Option::is_none")]
495        type_annotation: Option<IrType>,
496        init: Box<IrOperation>,
497        evidence: IrEvidence,
498    },
499
500    // === Binary operations ===
501    #[serde(rename = "binary")]
502    Binary {
503        operator: BinaryOp,
504        left: Box<IrOperation>,
505        right: Box<IrOperation>,
506        #[serde(rename = "type")]
507        ty: IrType,
508        evidence: IrEvidence,
509    },
510
511    // === Unary operations ===
512    #[serde(rename = "unary")]
513    Unary {
514        operator: UnaryOp,
515        operand: Box<IrOperation>,
516        #[serde(rename = "type")]
517        ty: IrType,
518        evidence: IrEvidence,
519    },
520
521    // === Function calls ===
522    #[serde(rename = "call")]
523    Call {
524        function: String,
525        function_id: String,
526        args: Vec<IrOperation>,
527        #[serde(skip_serializing_if = "Vec::is_empty")]
528        type_args: Vec<IrType>,
529        #[serde(rename = "type")]
530        ty: IrType,
531        evidence: IrEvidence,
532    },
533
534    #[serde(rename = "method_call")]
535    MethodCall {
536        receiver: Box<IrOperation>,
537        method: String,
538        args: Vec<IrOperation>,
539        #[serde(skip_serializing_if = "Vec::is_empty")]
540        type_args: Vec<IrType>,
541        #[serde(rename = "type")]
542        ty: IrType,
543        evidence: IrEvidence,
544    },
545
546    // === Closures ===
547    #[serde(rename = "closure")]
548    Closure {
549        params: Vec<IrParam>,
550        body: Box<IrOperation>,
551        captures: Vec<String>,
552        #[serde(rename = "type")]
553        ty: IrType,
554        evidence: IrEvidence,
555    },
556
557    // === Control flow ===
558    #[serde(rename = "if")]
559    If {
560        condition: Box<IrOperation>,
561        then_branch: Box<IrOperation>,
562        #[serde(skip_serializing_if = "Option::is_none")]
563        else_branch: Option<Box<IrOperation>>,
564        #[serde(rename = "type")]
565        ty: IrType,
566        evidence: IrEvidence,
567    },
568
569    #[serde(rename = "match")]
570    Match {
571        scrutinee: Box<IrOperation>,
572        arms: Vec<IrMatchArm>,
573        #[serde(rename = "type")]
574        ty: IrType,
575        evidence: IrEvidence,
576    },
577
578    /// Pattern match used as a boolean condition in if-let/while-let expressions.
579    /// Returns true if the pattern matches, false otherwise.
580    /// Pattern bindings are extracted in the then-branch.
581    #[serde(rename = "let_match")]
582    LetMatch {
583        pattern: IrPattern,
584        value: Box<IrOperation>,
585        #[serde(rename = "type")]
586        ty: IrType,
587        evidence: IrEvidence,
588    },
589
590    #[serde(rename = "loop")]
591    Loop {
592        variant: LoopVariant,
593        #[serde(skip_serializing_if = "Option::is_none")]
594        condition: Option<Box<IrOperation>>,
595        #[serde(skip_serializing_if = "Option::is_none")]
596        iterator: Option<IrForIterator>,
597        body: Box<IrOperation>,
598        #[serde(rename = "type")]
599        ty: IrType,
600        evidence: IrEvidence,
601    },
602
603    #[serde(rename = "break")]
604    Break {
605        #[serde(skip_serializing_if = "Option::is_none")]
606        value: Option<Box<IrOperation>>,
607        evidence: IrEvidence,
608    },
609
610    #[serde(rename = "continue")]
611    Continue { evidence: IrEvidence },
612
613    #[serde(rename = "return")]
614    Return {
615        #[serde(skip_serializing_if = "Option::is_none")]
616        value: Option<Box<IrOperation>>,
617        evidence: IrEvidence,
618    },
619
620    // === Blocks ===
621    #[serde(rename = "block")]
622    Block {
623        statements: Vec<IrOperation>,
624        #[serde(rename = "type")]
625        ty: IrType,
626        evidence: IrEvidence,
627    },
628
629    // === Pipeline operations (core Sigil feature) ===
630    #[serde(rename = "pipeline")]
631    Pipeline {
632        input: Box<IrOperation>,
633        steps: Vec<IrPipelineStep>,
634        #[serde(rename = "type")]
635        ty: IrType,
636        evidence: IrEvidence,
637    },
638
639    // === Morpheme operations ===
640    #[serde(rename = "morpheme")]
641    Morpheme {
642        morpheme: MorphemeKind,
643        symbol: String,
644        input: Box<IrOperation>,
645        #[serde(skip_serializing_if = "Option::is_none")]
646        body: Option<Box<IrOperation>>,
647        #[serde(rename = "type")]
648        ty: IrType,
649        evidence: IrEvidence,
650    },
651
652    // === Fork/Join ===
653    #[serde(rename = "fork")]
654    Fork {
655        branches: Vec<IrOperation>,
656        join_strategy: JoinStrategy,
657        #[serde(rename = "type")]
658        ty: IrType,
659        evidence: IrEvidence,
660    },
661
662    #[serde(rename = "identity")]
663    Identity {
664        #[serde(rename = "type")]
665        ty: IrType,
666        evidence: IrEvidence,
667    },
668
669    // === Data structures ===
670    #[serde(rename = "array")]
671    Array {
672        elements: Vec<IrOperation>,
673        #[serde(rename = "type")]
674        ty: IrType,
675        evidence: IrEvidence,
676    },
677
678    #[serde(rename = "tuple")]
679    Tuple {
680        elements: Vec<IrOperation>,
681        #[serde(rename = "type")]
682        ty: IrType,
683        evidence: IrEvidence,
684    },
685
686    #[serde(rename = "struct_init")]
687    StructInit {
688        name: String,
689        fields: Vec<(String, IrOperation)>,
690        #[serde(skip_serializing_if = "Option::is_none")]
691        rest: Option<Box<IrOperation>>,
692        #[serde(rename = "type")]
693        ty: IrType,
694        evidence: IrEvidence,
695    },
696
697    // === Field/Index access ===
698    #[serde(rename = "field")]
699    Field {
700        expr: Box<IrOperation>,
701        field: String,
702        #[serde(rename = "type")]
703        ty: IrType,
704        evidence: IrEvidence,
705    },
706
707    #[serde(rename = "index")]
708    Index {
709        expr: Box<IrOperation>,
710        index: Box<IrOperation>,
711        #[serde(rename = "type")]
712        ty: IrType,
713        evidence: IrEvidence,
714    },
715
716    // === Assignment ===
717    #[serde(rename = "assign")]
718    Assign {
719        target: Box<IrOperation>,
720        value: Box<IrOperation>,
721        evidence: IrEvidence,
722    },
723
724    // === Evidentiality operations ===
725    #[serde(rename = "evidence_coerce")]
726    EvidenceCoerce {
727        operation: EvidenceOp,
728        expr: Box<IrOperation>,
729        from_evidence: IrEvidence,
730        to_evidence: IrEvidence,
731        #[serde(rename = "type")]
732        ty: IrType,
733    },
734
735    // === Incorporation (noun-verb fusion) ===
736    #[serde(rename = "incorporation")]
737    Incorporation {
738        segments: Vec<IncorporationSegment>,
739        args: Vec<IrOperation>,
740        #[serde(rename = "type")]
741        ty: IrType,
742        evidence: IrEvidence,
743    },
744
745    // === Affect (emotional markers) ===
746    #[serde(rename = "affect")]
747    Affect {
748        expr: Box<IrOperation>,
749        affect: IrAffect,
750        #[serde(rename = "type")]
751        ty: IrType,
752        evidence: IrEvidence,
753    },
754
755    // === Protocol operations ===
756    #[serde(rename = "http_request")]
757    HttpRequest {
758        method: HttpMethod,
759        url: Box<IrOperation>,
760        #[serde(skip_serializing_if = "Option::is_none")]
761        headers: Option<Box<IrOperation>>,
762        #[serde(skip_serializing_if = "Option::is_none")]
763        body: Option<Box<IrOperation>>,
764        #[serde(skip_serializing_if = "Option::is_none")]
765        timeout: Option<Box<IrOperation>>,
766        #[serde(rename = "type")]
767        ty: IrType,
768        evidence: IrEvidence,
769    },
770
771    #[serde(rename = "grpc_call")]
772    GrpcCall {
773        service: String,
774        method: String,
775        message: Box<IrOperation>,
776        #[serde(skip_serializing_if = "Option::is_none")]
777        metadata: Option<Box<IrOperation>>,
778        #[serde(skip_serializing_if = "Option::is_none")]
779        timeout: Option<Box<IrOperation>>,
780        #[serde(rename = "type")]
781        ty: IrType,
782        evidence: IrEvidence,
783    },
784
785    #[serde(rename = "websocket")]
786    WebSocket {
787        operation: WebSocketOp,
788        url: Box<IrOperation>,
789        #[serde(skip_serializing_if = "Option::is_none")]
790        message: Option<Box<IrOperation>>,
791        #[serde(rename = "type")]
792        ty: IrType,
793        evidence: IrEvidence,
794    },
795
796    #[serde(rename = "kafka_op")]
797    KafkaOp {
798        operation: KafkaOpKind,
799        topic: String,
800        #[serde(skip_serializing_if = "Option::is_none")]
801        payload: Option<Box<IrOperation>>,
802        #[serde(skip_serializing_if = "Option::is_none")]
803        key: Option<Box<IrOperation>>,
804        #[serde(rename = "type")]
805        ty: IrType,
806        evidence: IrEvidence,
807    },
808
809    // === Async ===
810    #[serde(rename = "await")]
811    Await {
812        expr: Box<IrOperation>,
813        #[serde(rename = "type")]
814        ty: IrType,
815        evidence: IrEvidence,
816    },
817
818    // === Unsafe ===
819    #[serde(rename = "unsafe")]
820    Unsafe {
821        body: Box<IrOperation>,
822        #[serde(rename = "type")]
823        ty: IrType,
824        evidence: IrEvidence,
825    },
826
827    // === Async ===
828    #[serde(rename = "async")]
829    Async {
830        body: Box<IrOperation>,
831        is_move: bool,
832        #[serde(rename = "type")]
833        ty: IrType,
834        evidence: IrEvidence,
835    },
836
837    // === Cast ===
838    #[serde(rename = "cast")]
839    Cast {
840        expr: Box<IrOperation>,
841        target_type: IrType,
842        #[serde(rename = "type")]
843        ty: IrType,
844        evidence: IrEvidence,
845    },
846
847    // === Try (? operator) ===
848    #[serde(rename = "try")]
849    Try {
850        expr: Box<IrOperation>,
851        #[serde(rename = "type")]
852        ty: IrType,
853        evidence: IrEvidence,
854    },
855}
856
857/// Literal variants
858#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
859#[serde(rename_all = "lowercase")]
860pub enum LiteralVariant {
861    Int,
862    Float,
863    Bool,
864    Char,
865    String,
866    Null,
867}
868
869/// Binary operators
870#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
871#[serde(rename_all = "snake_case")]
872pub enum BinaryOp {
873    Add,
874    Sub,
875    Mul,
876    Div,
877    Rem,
878    Pow,
879    And,
880    Or,
881    BitAnd,
882    BitOr,
883    BitXor,
884    Shl,
885    Shr,
886    Eq,
887    Ne,
888    Lt,
889    Le,
890    Gt,
891    Ge,
892    Concat,
893    MatMul,
894    Hadamard,
895    TensorProd,
896    Convolve,
897}
898
899/// Unary operators
900#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
901#[serde(rename_all = "snake_case")]
902pub enum UnaryOp {
903    Neg,
904    Not,
905    Deref,
906    Ref,
907    RefMut,
908}
909
910/// Loop variants
911#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
912#[serde(rename_all = "lowercase")]
913pub enum LoopVariant {
914    Infinite,
915    While,
916    For,
917}
918
919/// For loop iterator
920#[derive(Debug, Clone, Serialize, Deserialize)]
921pub struct IrForIterator {
922    pub pattern: IrPattern,
923    pub iterable: Box<IrOperation>,
924}
925
926/// Match arm
927#[derive(Debug, Clone, Serialize, Deserialize)]
928pub struct IrMatchArm {
929    pub pattern: IrPattern,
930    #[serde(skip_serializing_if = "Option::is_none")]
931    pub guard: Option<IrOperation>,
932    pub body: IrOperation,
933}
934
935/// Pattern in IR
936#[derive(Debug, Clone, Serialize, Deserialize)]
937#[serde(tag = "kind")]
938pub enum IrPattern {
939    #[serde(rename = "ident")]
940    Ident {
941        name: String,
942        mutable: bool,
943        #[serde(skip_serializing_if = "Option::is_none")]
944        evidence: Option<IrEvidence>,
945    },
946
947    #[serde(rename = "tuple")]
948    Tuple { elements: Vec<IrPattern> },
949
950    #[serde(rename = "struct")]
951    Struct {
952        path: String,
953        fields: Vec<(String, IrPattern)>,
954        rest: bool,
955    },
956
957    #[serde(rename = "tuple_struct")]
958    TupleStruct {
959        path: String,
960        fields: Vec<IrPattern>,
961    },
962
963    #[serde(rename = "slice")]
964    Slice { elements: Vec<IrPattern> },
965
966    #[serde(rename = "or")]
967    Or { patterns: Vec<IrPattern> },
968
969    #[serde(rename = "literal")]
970    Literal { value: serde_json::Value },
971
972    #[serde(rename = "range")]
973    Range {
974        #[serde(skip_serializing_if = "Option::is_none")]
975        start: Option<Box<IrPattern>>,
976        #[serde(skip_serializing_if = "Option::is_none")]
977        end: Option<Box<IrPattern>>,
978        inclusive: bool,
979    },
980
981    #[serde(rename = "wildcard")]
982    Wildcard,
983}
984
985/// Pipeline step
986#[derive(Debug, Clone, Serialize, Deserialize)]
987#[serde(tag = "op")]
988pub enum IrPipelineStep {
989    #[serde(rename = "call")]
990    Call {
991        #[serde(rename = "fn")]
992        function: String,
993        args: Vec<IrOperation>,
994    },
995
996    #[serde(rename = "morpheme")]
997    Morpheme {
998        morpheme: MorphemeKind,
999        symbol: String,
1000        #[serde(skip_serializing_if = "Option::is_none")]
1001        body: Option<Box<IrOperation>>,
1002    },
1003
1004    #[serde(rename = "fork")]
1005    Fork { branches: Vec<IrPipelineStep> },
1006
1007    #[serde(rename = "identity")]
1008    Identity,
1009
1010    #[serde(rename = "method")]
1011    Method {
1012        name: String,
1013        args: Vec<IrOperation>,
1014    },
1015
1016    #[serde(rename = "await")]
1017    Await,
1018
1019    #[serde(rename = "protocol")]
1020    Protocol {
1021        operation: ProtocolOp,
1022        #[serde(skip_serializing_if = "Option::is_none")]
1023        config: Option<Box<IrOperation>>,
1024    },
1025}
1026
1027/// Morpheme kinds
1028#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1029#[serde(rename_all = "lowercase")]
1030pub enum MorphemeKind {
1031    Transform,
1032    Filter,
1033    Sort,
1034    Reduce,
1035    Lambda,
1036    Sum,
1037    Product,
1038    Min,
1039    Max,
1040    Concat,
1041    All,
1042    Any,
1043    First,
1044    Last,
1045    Middle,
1046    Choice,
1047    Nth,
1048    Next,
1049}
1050
1051impl MorphemeKind {
1052    pub fn symbol(&self) -> &'static str {
1053        match self {
1054            MorphemeKind::Transform => "τ",
1055            MorphemeKind::Filter => "φ",
1056            MorphemeKind::Sort => "σ",
1057            MorphemeKind::Reduce => "ρ",
1058            MorphemeKind::Lambda => "λ",
1059            MorphemeKind::Sum => "Σ",
1060            MorphemeKind::Product => "Π",
1061            MorphemeKind::Min => "ρ_min",
1062            MorphemeKind::Max => "ρ_max",
1063            MorphemeKind::Concat => "ρ++",
1064            MorphemeKind::All => "ρ&",
1065            MorphemeKind::Any => "ρ|",
1066            MorphemeKind::First => "α",
1067            MorphemeKind::Last => "ω",
1068            MorphemeKind::Middle => "μ",
1069            MorphemeKind::Choice => "χ",
1070            MorphemeKind::Nth => "ν",
1071            MorphemeKind::Next => "ξ",
1072        }
1073    }
1074}
1075
1076/// Join strategy for fork operations
1077#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1078#[serde(rename_all = "lowercase")]
1079pub enum JoinStrategy {
1080    Tuple,
1081    First,
1082    All,
1083    Race,
1084}
1085
1086/// Evidence operations
1087#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1088#[serde(rename_all = "lowercase")]
1089pub enum EvidenceOp {
1090    Trust,
1091    Verify,
1092    Mark,
1093}
1094
1095/// Incorporation segment (noun-verb)
1096#[derive(Debug, Clone, Serialize, Deserialize)]
1097#[serde(tag = "kind")]
1098pub enum IncorporationSegment {
1099    #[serde(rename = "noun")]
1100    Noun { name: String },
1101    #[serde(rename = "verb")]
1102    Verb { name: String },
1103}
1104
1105/// Affect markers
1106#[derive(Debug, Clone, Serialize, Deserialize)]
1107pub struct IrAffect {
1108    #[serde(skip_serializing_if = "Option::is_none")]
1109    pub sentiment: Option<Sentiment>,
1110    #[serde(skip_serializing_if = "Option::is_none")]
1111    pub intensity: Option<Intensity>,
1112    #[serde(skip_serializing_if = "Option::is_none")]
1113    pub formality: Option<Formality>,
1114    #[serde(skip_serializing_if = "Option::is_none")]
1115    pub emotion: Option<Emotion>,
1116    #[serde(skip_serializing_if = "Option::is_none")]
1117    pub confidence: Option<Confidence>,
1118    #[serde(default)]
1119    pub sarcasm: bool,
1120}
1121
1122#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1123#[serde(rename_all = "lowercase")]
1124pub enum Sentiment {
1125    Positive,
1126    Negative,
1127    Neutral,
1128}
1129
1130#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1131#[serde(rename_all = "lowercase")]
1132pub enum Intensity {
1133    Up,
1134    Down,
1135    Max,
1136}
1137
1138#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1139#[serde(rename_all = "lowercase")]
1140pub enum Formality {
1141    Formal,
1142    Informal,
1143}
1144
1145#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1146#[serde(rename_all = "lowercase")]
1147pub enum Emotion {
1148    Joy,
1149    Sadness,
1150    Anger,
1151    Fear,
1152    Surprise,
1153    Love,
1154}
1155
1156#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1157#[serde(rename_all = "lowercase")]
1158pub enum Confidence {
1159    High,
1160    Medium,
1161    Low,
1162}
1163
1164/// HTTP methods
1165#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1166#[serde(rename_all = "UPPERCASE")]
1167pub enum HttpMethod {
1168    Get,
1169    Post,
1170    Put,
1171    Delete,
1172    Patch,
1173    Head,
1174    Options,
1175    Connect,
1176    Trace,
1177}
1178
1179/// WebSocket operations
1180#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1181#[serde(rename_all = "lowercase")]
1182pub enum WebSocketOp {
1183    Connect,
1184    Send,
1185    Receive,
1186    Close,
1187}
1188
1189/// Kafka operations
1190#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1191#[serde(rename_all = "lowercase")]
1192pub enum KafkaOpKind {
1193    Produce,
1194    Consume,
1195    Subscribe,
1196    Unsubscribe,
1197    Commit,
1198    Seek,
1199}
1200
1201/// Protocol operations for pipeline
1202#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1203#[serde(rename_all = "lowercase")]
1204pub enum ProtocolOp {
1205    Send,
1206    Recv,
1207    Stream,
1208    Connect,
1209    Close,
1210    Timeout,
1211    Retry,
1212}
1213
1214/// Configuration options for IR dump
1215#[derive(Debug, Clone, Default)]
1216pub struct IrDumpOptions {
1217    /// Pretty-print JSON output
1218    pub pretty: bool,
1219    /// Include full type information on every node
1220    pub full_types: bool,
1221    /// Include source spans
1222    pub include_spans: bool,
1223    /// Optimization level to apply before dumping
1224    pub opt_level: Option<u8>,
1225}
1226
1227impl IrModule {
1228    /// Serialize to JSON string
1229    pub fn to_json(&self, pretty: bool) -> Result<String, serde_json::Error> {
1230        if pretty {
1231            serde_json::to_string_pretty(self)
1232        } else {
1233            serde_json::to_string(self)
1234        }
1235    }
1236
1237    /// Deserialize from JSON string
1238    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
1239        serde_json::from_str(json)
1240    }
1241}
1242
1243#[cfg(test)]
1244mod tests {
1245    use super::*;
1246
1247    #[test]
1248    fn test_evidence_join() {
1249        assert_eq!(IrEvidence::Known.join(IrEvidence::Known), IrEvidence::Known);
1250        assert_eq!(
1251            IrEvidence::Known.join(IrEvidence::Uncertain),
1252            IrEvidence::Uncertain
1253        );
1254        assert_eq!(
1255            IrEvidence::Known.join(IrEvidence::Reported),
1256            IrEvidence::Reported
1257        );
1258        assert_eq!(
1259            IrEvidence::Known.join(IrEvidence::Paradox),
1260            IrEvidence::Paradox
1261        );
1262        assert_eq!(
1263            IrEvidence::Reported.join(IrEvidence::Paradox),
1264            IrEvidence::Paradox
1265        );
1266    }
1267
1268    #[test]
1269    fn test_evidence_meet() {
1270        assert_eq!(
1271            IrEvidence::Paradox.meet(IrEvidence::Paradox),
1272            IrEvidence::Paradox
1273        );
1274        assert_eq!(
1275            IrEvidence::Paradox.meet(IrEvidence::Reported),
1276            IrEvidence::Reported
1277        );
1278        assert_eq!(
1279            IrEvidence::Paradox.meet(IrEvidence::Uncertain),
1280            IrEvidence::Uncertain
1281        );
1282        assert_eq!(
1283            IrEvidence::Paradox.meet(IrEvidence::Known),
1284            IrEvidence::Known
1285        );
1286    }
1287
1288    #[test]
1289    fn test_ir_serialization() {
1290        let module = IrModule::new("test.sigil".to_string());
1291        let json = module.to_json(false).unwrap();
1292        let parsed: IrModule = IrModule::from_json(&json).unwrap();
1293        assert_eq!(parsed.version, IR_VERSION);
1294        assert_eq!(parsed.source, "test.sigil");
1295    }
1296
1297    #[test]
1298    fn test_morpheme_symbols() {
1299        assert_eq!(MorphemeKind::Transform.symbol(), "τ");
1300        assert_eq!(MorphemeKind::Filter.symbol(), "φ");
1301        assert_eq!(MorphemeKind::Reduce.symbol(), "ρ");
1302    }
1303}