Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/frontend/ast.rs
Line
Count
Source
1
//! Abstract Syntax Tree definitions for Ruchy
2
3
use serde::{Deserialize, Serialize};
4
use std::fmt;
5
6
/// Source location tracking for error reporting
7
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
8
pub struct Span {
9
    pub start: usize,
10
    pub end: usize,
11
}
12
13
impl Span {
14
    #[must_use]
15
0
    pub fn new(start: usize, end: usize) -> Self {
16
0
        Self { start, end }
17
0
    }
18
19
    #[must_use]
20
0
    pub fn merge(self, other: Self) -> Self {
21
0
        Self {
22
0
            start: self.start.min(other.start),
23
0
            end: self.end.max(other.end),
24
0
        }
25
0
    }
26
}
27
28
/// Catch clause in try-catch blocks
29
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30
pub struct CatchClause {
31
    pub pattern: Pattern,  // The error pattern to match
32
    pub body: Box<Expr>,    // The catch block body
33
}
34
35
/// The main AST node type for expressions
36
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37
pub struct Expr {
38
    pub kind: ExprKind,
39
    pub span: Span,
40
    pub attributes: Vec<Attribute>,
41
}
42
43
impl Expr {
44
    #[must_use]
45
0
    pub fn new(kind: ExprKind, span: Span) -> Self {
46
0
        Self {
47
0
            kind,
48
0
            span,
49
0
            attributes: Vec::new(),
50
0
        }
51
0
    }
52
53
    #[must_use]
54
0
    pub fn with_attributes(kind: ExprKind, span: Span, attributes: Vec<Attribute>) -> Self {
55
0
        Self {
56
0
            kind,
57
0
            span,
58
0
            attributes,
59
0
        }
60
0
    }
61
}
62
63
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
64
pub enum ExprKind {
65
    Literal(Literal),
66
    Identifier(String),
67
    QualifiedName {
68
        module: String,
69
        name: String,
70
    },
71
    StringInterpolation {
72
        parts: Vec<StringPart>,
73
    },
74
    Binary {
75
        left: Box<Expr>,
76
        op: BinaryOp,
77
        right: Box<Expr>,
78
    },
79
    Unary {
80
        op: UnaryOp,
81
        operand: Box<Expr>,
82
    },
83
    Throw {
84
        expr: Box<Expr>,
85
    },
86
    TryCatch {
87
        try_block: Box<Expr>,
88
        catch_clauses: Vec<CatchClause>,
89
        finally_block: Option<Box<Expr>>,
90
    },
91
    Ok {
92
        value: Box<Expr>,
93
    },
94
    Err {
95
        error: Box<Expr>,
96
    },
97
    Some {
98
        value: Box<Expr>,
99
    },
100
    None,
101
    TypeCast {
102
        expr: Box<Expr>,
103
        target_type: String,
104
    },
105
    Try {
106
        expr: Box<Expr>,
107
    },
108
    Await {
109
        expr: Box<Expr>,
110
    },
111
    AsyncBlock {
112
        body: Box<Expr>,
113
    },
114
    If {
115
        condition: Box<Expr>,
116
        then_branch: Box<Expr>,
117
        else_branch: Option<Box<Expr>>,
118
    },
119
    IfLet {
120
        pattern: Pattern,
121
        expr: Box<Expr>,
122
        then_branch: Box<Expr>,
123
        else_branch: Option<Box<Expr>>,
124
    },
125
    Let {
126
        name: String,
127
        type_annotation: Option<Type>,
128
        value: Box<Expr>,
129
        body: Box<Expr>,
130
        is_mutable: bool,
131
    },
132
    LetPattern {
133
        pattern: Pattern,
134
        type_annotation: Option<Type>,
135
        value: Box<Expr>,
136
        body: Box<Expr>,
137
        is_mutable: bool,
138
    },
139
    Function {
140
        name: String,
141
        type_params: Vec<String>,
142
        params: Vec<Param>,
143
        return_type: Option<Type>,
144
        body: Box<Expr>,
145
        is_async: bool,
146
        is_pub: bool,
147
    },
148
    Lambda {
149
        params: Vec<Param>,
150
        body: Box<Expr>,
151
    },
152
    Struct {
153
        name: String,
154
        type_params: Vec<String>,
155
        fields: Vec<StructField>,
156
        is_pub: bool,
157
    },
158
    Enum {
159
        name: String,
160
        type_params: Vec<String>,
161
        variants: Vec<EnumVariant>,
162
        is_pub: bool,
163
    },
164
    StructLiteral {
165
        name: String,
166
        fields: Vec<(String, Expr)>,
167
    },
168
    ObjectLiteral {
169
        fields: Vec<ObjectField>,
170
    },
171
    FieldAccess {
172
        object: Box<Expr>,
173
        field: String,
174
    },
175
    OptionalFieldAccess {
176
        object: Box<Expr>,
177
        field: String,
178
    },
179
    IndexAccess {
180
        object: Box<Expr>,
181
        index: Box<Expr>,
182
    },
183
    Slice {
184
        object: Box<Expr>,
185
        start: Option<Box<Expr>>,
186
        end: Option<Box<Expr>>,
187
    },
188
    Trait {
189
        name: String,
190
        type_params: Vec<String>,
191
        methods: Vec<TraitMethod>,
192
        is_pub: bool,
193
    },
194
    Impl {
195
        type_params: Vec<String>,
196
        trait_name: Option<String>,
197
        for_type: String,
198
        methods: Vec<ImplMethod>,
199
        is_pub: bool,
200
    },
201
    Actor {
202
        name: String,
203
        state: Vec<StructField>,
204
        handlers: Vec<ActorHandler>,
205
    },
206
    Send {
207
        actor: Box<Expr>,
208
        message: Box<Expr>,
209
    },
210
    Command {
211
        program: String,
212
        args: Vec<String>,
213
        env: Vec<(String, String)>,
214
        working_dir: Option<String>,
215
    },
216
    Ask {
217
        actor: Box<Expr>,
218
        message: Box<Expr>,
219
        timeout: Option<Box<Expr>>,
220
    },
221
    /// Fire-and-forget actor send (left <- right)
222
    ActorSend {
223
        actor: Box<Expr>,
224
        message: Box<Expr>,
225
    },
226
    /// Actor query with reply (left <? right)
227
    ActorQuery {
228
        actor: Box<Expr>,
229
        message: Box<Expr>,
230
    },
231
    Call {
232
        func: Box<Expr>,
233
        args: Vec<Expr>,
234
    },
235
    Macro {
236
        name: String,
237
        args: Vec<Expr>,
238
    },
239
    MethodCall {
240
        receiver: Box<Expr>,
241
        method: String,
242
        args: Vec<Expr>,
243
    },
244
    OptionalMethodCall {
245
        receiver: Box<Expr>,
246
        method: String,
247
        args: Vec<Expr>,
248
    },
249
    Block(Vec<Expr>),
250
    Pipeline {
251
        expr: Box<Expr>,
252
        stages: Vec<PipelineStage>,
253
    },
254
    Match {
255
        expr: Box<Expr>,
256
        arms: Vec<MatchArm>,
257
    },
258
    List(Vec<Expr>),
259
    Tuple(Vec<Expr>),
260
    Spread {
261
        expr: Box<Expr>,
262
    },
263
    ListComprehension {
264
        element: Box<Expr>,
265
        variable: String,
266
        iterable: Box<Expr>,
267
        condition: Option<Box<Expr>>,
268
    },
269
    DataFrame {
270
        columns: Vec<DataFrameColumn>,
271
    },
272
    DataFrameOperation {
273
        source: Box<Expr>,
274
        operation: DataFrameOp,
275
    },
276
    For {
277
        var: String,  // Keep for backward compatibility
278
        pattern: Option<Pattern>,  // New: Support destructuring patterns
279
        iter: Box<Expr>,
280
        body: Box<Expr>,
281
    },
282
    While {
283
        condition: Box<Expr>,
284
        body: Box<Expr>,
285
    },
286
    WhileLet {
287
        pattern: Pattern,
288
        expr: Box<Expr>,
289
        body: Box<Expr>,
290
    },
291
    Loop {
292
        body: Box<Expr>,
293
    },
294
    Range {
295
        start: Box<Expr>,
296
        end: Box<Expr>,
297
        inclusive: bool,
298
    },
299
    Import {
300
        path: String,
301
        items: Vec<ImportItem>,
302
    },
303
    Module {
304
        name: String,
305
        body: Box<Expr>,
306
    },
307
    Export {
308
        items: Vec<String>,
309
    },
310
    Break {
311
        label: Option<String>,
312
    },
313
    Continue {
314
        label: Option<String>,
315
    },
316
    Return {
317
        value: Option<Box<Expr>>,
318
    },
319
    Assign {
320
        target: Box<Expr>,
321
        value: Box<Expr>,
322
    },
323
    CompoundAssign {
324
        target: Box<Expr>,
325
        op: BinaryOp,
326
        value: Box<Expr>,
327
    },
328
    PreIncrement {
329
        target: Box<Expr>,
330
    },
331
    PostIncrement {
332
        target: Box<Expr>,
333
    },
334
    PreDecrement {
335
        target: Box<Expr>,
336
    },
337
    PostDecrement {
338
        target: Box<Expr>,
339
    },
340
    Extension {
341
        target_type: String,
342
        methods: Vec<ImplMethod>,
343
    },
344
}
345
346
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
347
pub enum Literal {
348
    Integer(i64),
349
    Float(f64),
350
    String(String),
351
    Bool(bool),
352
    Char(char),
353
    Unit,
354
}
355
356
impl Literal {
357
    /// Convert a REPL Value to a Literal (for synthetic expressions)
358
0
    pub fn from_value(value: &crate::runtime::repl::Value) -> Self {
359
        use crate::runtime::repl::Value;
360
0
        match value {
361
0
            Value::Int(i) => Literal::Integer(*i),
362
0
            Value::Float(f) => Literal::Float(*f),
363
0
            Value::String(s) => Literal::String(s.clone()),
364
0
            Value::Bool(b) => Literal::Bool(*b),
365
0
            Value::Char(c) => Literal::Char(*c),
366
0
            Value::Unit => Literal::Unit,
367
0
            _ => Literal::Unit, // Fallback for complex types
368
        }
369
0
    }
370
}
371
372
/// String interpolation parts - either literal text or an expression
373
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
374
pub enum StringPart {
375
    /// Literal text portion of the string
376
    Text(String),
377
    /// Expression to be interpolated without format specifier
378
    Expr(Box<Expr>),
379
    /// Expression with format specifier (e.g., {value:.2})
380
    ExprWithFormat {
381
        expr: Box<Expr>,
382
        format_spec: String,
383
    },
384
}
385
386
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
387
pub enum BinaryOp {
388
    // Arithmetic
389
    Add,
390
    Subtract,
391
    Multiply,
392
    Divide,
393
    Modulo,
394
    Power,
395
396
    // Comparison
397
    Equal,
398
    NotEqual,
399
    Less,
400
    LessEqual,
401
    Greater,
402
    GreaterEqual,
403
404
    // Logical
405
    And,
406
    Or,
407
    NullCoalesce,
408
409
    // Bitwise
410
    BitwiseAnd,
411
    BitwiseOr,
412
    BitwiseXor,
413
    LeftShift,
414
}
415
416
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
417
pub enum UnaryOp {
418
    Not,
419
    Negate,
420
    BitwiseNot,
421
    Reference,
422
}
423
424
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
425
pub struct Param {
426
    pub pattern: Pattern,
427
    pub ty: Type,
428
    pub span: Span,
429
    pub is_mutable: bool,
430
    pub default_value: Option<Box<Expr>>,
431
}
432
433
impl Param {
434
    /// Get the primary name from this parameter pattern.
435
    /// For complex patterns, this returns the first/primary identifier.
436
    /// For simple patterns, this returns the identifier itself.
437
    #[must_use]
438
0
    pub fn name(&self) -> String {
439
0
        self.pattern.primary_name()
440
0
    }
441
}
442
443
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
444
pub struct StructField {
445
    pub name: String,
446
    pub ty: Type,
447
    pub is_pub: bool,
448
}
449
450
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
451
pub struct EnumVariant {
452
    pub name: String,
453
    pub fields: Option<Vec<Type>>, // None for unit variant, Some for tuple variant
454
    pub discriminant: Option<i64>, // Explicit discriminant value for TypeScript compatibility
455
}
456
457
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
458
pub enum ObjectField {
459
    KeyValue { key: String, value: Expr },
460
    Spread { expr: Expr },
461
}
462
463
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
464
pub struct TraitMethod {
465
    pub name: String,
466
    pub params: Vec<Param>,
467
    pub return_type: Option<Type>,
468
    pub body: Option<Box<Expr>>, // None for method signatures, Some for default implementations
469
    pub is_pub: bool,
470
}
471
472
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
473
pub struct ImplMethod {
474
    pub name: String,
475
    pub params: Vec<Param>,
476
    pub return_type: Option<Type>,
477
    pub body: Box<Expr>,
478
    pub is_pub: bool,
479
}
480
481
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
482
pub struct ActorHandler {
483
    pub message_type: String,
484
    pub params: Vec<Param>,
485
    pub body: Box<Expr>,
486
}
487
488
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
489
pub struct Type {
490
    pub kind: TypeKind,
491
    pub span: Span,
492
}
493
494
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
495
pub enum TypeKind {
496
    Named(String),
497
    Generic { base: String, params: Vec<Type> },
498
    Optional(Box<Type>),
499
    List(Box<Type>),
500
    Tuple(Vec<Type>),
501
    Function { params: Vec<Type>, ret: Box<Type> },
502
    DataFrame { columns: Vec<(String, Type)> },
503
    Series { dtype: Box<Type> },
504
    Reference { is_mut: bool, inner: Box<Type> },
505
}
506
507
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
508
pub struct PipelineStage {
509
    pub op: Box<Expr>,
510
    pub span: Span,
511
}
512
513
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
514
pub struct MatchArm {
515
    pub pattern: Pattern,
516
    pub guard: Option<Box<Expr>>,  // Pattern guard: if condition
517
    pub body: Box<Expr>,
518
    pub span: Span,
519
}
520
521
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
522
pub enum Pattern {
523
    Wildcard,
524
    Literal(Literal),
525
    Identifier(String),
526
    QualifiedName(Vec<String>), // For patterns like Ordering::Less
527
    Tuple(Vec<Pattern>),
528
    List(Vec<Pattern>),
529
    Struct {
530
        name: String,
531
        fields: Vec<StructPatternField>,
532
        has_rest: bool,
533
    },
534
    Range {
535
        start: Box<Pattern>,
536
        end: Box<Pattern>,
537
        inclusive: bool,
538
    },
539
    Or(Vec<Pattern>),
540
    Rest, // For ... patterns
541
    RestNamed(String), // For ..name patterns
542
    WithDefault {
543
        pattern: Box<Pattern>,
544
        default: Box<Expr>,
545
    }, // For patterns with default values like a = 10
546
    Ok(Box<Pattern>),
547
    Err(Box<Pattern>),
548
    Some(Box<Pattern>),
549
    None,
550
}
551
552
impl Pattern {
553
    /// Get the primary identifier name from this pattern.
554
    /// For complex patterns, returns the first/most significant identifier.
555
    #[must_use]
556
0
    pub fn primary_name(&self) -> String {
557
0
        match self {
558
0
            Pattern::Identifier(name) => name.clone(),
559
0
            Pattern::QualifiedName(path) => path.join("::"),
560
0
            Pattern::Tuple(patterns) => {
561
                // Return the name of the first pattern
562
0
                patterns
563
0
                    .first()
564
0
                    .map_or_else(|| "_tuple".to_string(), Pattern::primary_name)
565
            }
566
0
            Pattern::List(patterns) => {
567
                // Return the name of the first pattern
568
0
                patterns
569
0
                    .first()
570
0
                    .map_or_else(|| "_list".to_string(), Pattern::primary_name)
571
            }
572
0
            Pattern::Struct { name, fields, .. } => {
573
                // Return the struct type name, or first field name if anonymous
574
0
                if name.is_empty() {
575
0
                    fields.first().map_or_else(|| "_struct".to_string(), |f| f.name.clone())
576
                } else {
577
0
                    name.clone()
578
                }
579
            }
580
0
            Pattern::Ok(inner) | Pattern::Err(inner) | Pattern::Some(inner) => inner.primary_name(),
581
0
            Pattern::None => "_none".to_string(),
582
0
            Pattern::Or(patterns) => {
583
                // Return the name of the first pattern
584
0
                patterns
585
0
                    .first()
586
0
                    .map_or_else(|| "_or".to_string(), Pattern::primary_name)
587
            }
588
0
            Pattern::Wildcard => "_".to_string(),
589
0
            Pattern::Rest => "_rest".to_string(),
590
0
            Pattern::RestNamed(name) => name.clone(),
591
0
            Pattern::WithDefault { pattern, .. } => pattern.primary_name(),
592
0
            Pattern::Literal(lit) => format!("_literal_{lit:?}"),
593
0
            Pattern::Range { .. } => "_range".to_string(),
594
        }
595
0
    }
596
}
597
598
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
599
pub struct StructPatternField {
600
    pub name: String,
601
    pub pattern: Option<Pattern>, // None for shorthand like { x } instead of { x: x }
602
}
603
604
605
/// Custom error type definition
606
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
607
pub struct ErrorTypeDef {
608
    pub name: String,
609
    pub fields: Vec<StructField>,
610
    pub extends: Option<String>, // Parent error type
611
}
612
613
/// Attribute for annotating expressions (e.g., `#[property]`)
614
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
615
pub struct Attribute {
616
    pub name: String,
617
    pub args: Vec<String>,
618
    pub span: Span,
619
}
620
621
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
622
pub struct DataFrameColumn {
623
    pub name: String,
624
    pub values: Vec<Expr>,
625
}
626
627
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
628
pub enum DataFrameOp {
629
    Filter(Box<Expr>),
630
    Select(Vec<String>),
631
    GroupBy(Vec<String>),
632
    Sort(Vec<String>),
633
    Join {
634
        other: Box<Expr>,
635
        on: Vec<String>,
636
        how: JoinType,
637
    },
638
    Aggregate(Vec<AggregateOp>),
639
    Limit(usize),
640
    Head(usize),
641
    Tail(usize),
642
}
643
644
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
645
pub enum JoinType {
646
    Inner,
647
    Left,
648
    Right,
649
    Outer,
650
}
651
652
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
653
pub enum ImportItem {
654
    /// Import a specific name: `use std::collections::HashMap`
655
    Named(String),
656
    /// Import with alias: `use std::collections::HashMap as Map`
657
    Aliased { name: String, alias: String },
658
    /// Import all: `use std::collections::*`
659
    Wildcard,
660
}
661
662
impl ImportItem {
663
    /// Check if this import is for a URL module
664
0
    pub fn is_url_import(path: &str) -> bool {
665
0
        path.starts_with("https://") || path.starts_with("http://")
666
0
    }
667
}
668
669
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
670
pub enum AggregateOp {
671
    Sum(String),
672
    Mean(String),
673
    Min(String),
674
    Max(String),
675
    Count(String),
676
    Std(String),
677
    Var(String),
678
}
679
680
impl fmt::Display for BinaryOp {
681
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682
0
        match self {
683
0
            Self::Add => write!(f, "+"),
684
0
            Self::Subtract => write!(f, "-"),
685
0
            Self::Multiply => write!(f, "*"),
686
0
            Self::Divide => write!(f, "/"),
687
0
            Self::Modulo => write!(f, "%"),
688
0
            Self::Power => write!(f, "**"),
689
0
            Self::Equal => write!(f, "=="),
690
0
            Self::NotEqual => write!(f, "!="),
691
0
            Self::Less => write!(f, "<"),
692
0
            Self::LessEqual => write!(f, "<="),
693
0
            Self::Greater => write!(f, ">"),
694
0
            Self::GreaterEqual => write!(f, ">="),
695
0
            Self::And => write!(f, "&&"),
696
0
            Self::Or => write!(f, "||"),
697
0
            Self::NullCoalesce => write!(f, "??"),
698
0
            Self::BitwiseAnd => write!(f, "&"),
699
0
            Self::BitwiseOr => write!(f, "|"),
700
0
            Self::BitwiseXor => write!(f, "^"),
701
0
            Self::LeftShift => write!(f, "<<"),
702
        }
703
0
    }
704
}
705
706
impl fmt::Display for UnaryOp {
707
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
708
0
        match self {
709
0
            Self::Not => write!(f, "!"),
710
0
            Self::Negate => write!(f, "-"),
711
0
            Self::BitwiseNot => write!(f, "~"),
712
0
            Self::Reference => write!(f, "&"),
713
        }
714
0
    }
715
}
716
717
#[cfg(test)]
718
#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
719
#[allow(clippy::unwrap_used)]
720
#[allow(clippy::panic)]
721
mod tests {
722
    use super::*;
723
    use proptest::prelude::*;
724
725
    proptest! {
726
        #[test]
727
        fn test_span_merge(start1 in 0usize..1000, end1 in 0usize..1000,
728
                          start2 in 0usize..1000, end2 in 0usize..1000) {
729
            let span1 = Span::new(start1, end1);
730
            let span2 = Span::new(start2, end2);
731
            let merged = span1.merge(span2);
732
733
            prop_assert!(merged.start <= span1.start);
734
            prop_assert!(merged.start <= span2.start);
735
            prop_assert!(merged.end >= span1.end);
736
            prop_assert!(merged.end >= span2.end);
737
        }
738
    }
739
740
    #[test]
741
    fn test_ast_size() {
742
        // Track AST node sizes for optimization
743
        let expr_size = std::mem::size_of::<Expr>();
744
        let kind_size = std::mem::size_of::<ExprKind>();
745
        // Current sizes are larger than ideal but acceptable for MVP
746
        // Future optimization: Use arena allocation and indices
747
        assert!(expr_size <= 192, "Expr too large: {expr_size} bytes");
748
        assert!(kind_size <= 152, "ExprKind too large: {kind_size} bytes");
749
    }
750
751
    #[test]
752
    fn test_span_creation() {
753
        let span = Span::new(10, 20);
754
        assert_eq!(span.start, 10);
755
        assert_eq!(span.end, 20);
756
    }
757
758
    #[test]
759
    fn test_span_merge_simple() {
760
        let span1 = Span::new(5, 10);
761
        let span2 = Span::new(8, 15);
762
        let merged = span1.merge(span2);
763
        assert_eq!(merged.start, 5);
764
        assert_eq!(merged.end, 15);
765
    }
766
767
    #[test]
768
    fn test_span_merge_disjoint() {
769
        let span1 = Span::new(0, 5);
770
        let span2 = Span::new(10, 15);
771
        let merged = span1.merge(span2);
772
        assert_eq!(merged.start, 0);
773
        assert_eq!(merged.end, 15);
774
    }
775
776
    #[test]
777
    fn test_expr_creation() {
778
        let span = Span::new(0, 10);
779
        let expr = Expr::new(ExprKind::Literal(Literal::Integer(42)), span);
780
        assert_eq!(expr.span.start, 0);
781
        assert_eq!(expr.span.end, 10);
782
        match expr.kind {
783
            ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 42),
784
            _ => panic!("Wrong expression kind"),
785
        }
786
    }
787
788
    #[test]
789
    fn test_literal_variants() {
790
        let literals = vec![
791
            Literal::Integer(42),
792
            #[allow(clippy::approx_constant)]
793
            Literal::Float(3.14), // Not PI, just a test value
794
            Literal::String("hello".to_string()),
795
            Literal::Bool(true),
796
            Literal::Unit,
797
        ];
798
799
        for lit in literals {
800
            let expr = Expr::new(ExprKind::Literal(lit.clone()), Span::new(0, 0));
801
            match expr.kind {
802
                ExprKind::Literal(l) => assert_eq!(l, lit),
803
                _ => panic!("Expected literal"),
804
            }
805
        }
806
    }
807
808
    #[test]
809
    fn test_binary_op_display() {
810
        assert_eq!(BinaryOp::Add.to_string(), "+");
811
        assert_eq!(BinaryOp::Subtract.to_string(), "-");
812
        assert_eq!(BinaryOp::Multiply.to_string(), "*");
813
        assert_eq!(BinaryOp::Divide.to_string(), "/");
814
        assert_eq!(BinaryOp::Modulo.to_string(), "%");
815
        assert_eq!(BinaryOp::Power.to_string(), "**");
816
        assert_eq!(BinaryOp::Equal.to_string(), "==");
817
        assert_eq!(BinaryOp::NotEqual.to_string(), "!=");
818
        assert_eq!(BinaryOp::Less.to_string(), "<");
819
        assert_eq!(BinaryOp::LessEqual.to_string(), "<=");
820
        assert_eq!(BinaryOp::Greater.to_string(), ">");
821
        assert_eq!(BinaryOp::GreaterEqual.to_string(), ">=");
822
        assert_eq!(BinaryOp::And.to_string(), "&&");
823
        assert_eq!(BinaryOp::Or.to_string(), "||");
824
        assert_eq!(BinaryOp::BitwiseAnd.to_string(), "&");
825
        assert_eq!(BinaryOp::BitwiseOr.to_string(), "|");
826
        assert_eq!(BinaryOp::BitwiseXor.to_string(), "^");
827
        assert_eq!(BinaryOp::LeftShift.to_string(), "<<");
828
    }
829
830
    #[test]
831
    fn test_unary_op_display() {
832
        assert_eq!(UnaryOp::Not.to_string(), "!");
833
        assert_eq!(UnaryOp::Negate.to_string(), "-");
834
        assert_eq!(UnaryOp::BitwiseNot.to_string(), "~");
835
        assert_eq!(UnaryOp::Reference.to_string(), "&");
836
    }
837
838
    #[test]
839
    fn test_binary_expression() {
840
        let left = Box::new(Expr::new(
841
            ExprKind::Literal(Literal::Integer(1)),
842
            Span::new(0, 1),
843
        ));
844
        let right = Box::new(Expr::new(
845
            ExprKind::Literal(Literal::Integer(2)),
846
            Span::new(4, 5),
847
        ));
848
        let expr = Expr::new(
849
            ExprKind::Binary {
850
                left,
851
                op: BinaryOp::Add,
852
                right,
853
            },
854
            Span::new(0, 5),
855
        );
856
857
        match expr.kind {
858
            ExprKind::Binary {
859
                left: l,
860
                op,
861
                right: r,
862
            } => {
863
                assert_eq!(op, BinaryOp::Add);
864
                match l.kind {
865
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 1),
866
                    _ => panic!("Wrong left operand"),
867
                }
868
                match r.kind {
869
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 2),
870
                    _ => panic!("Wrong right operand"),
871
                }
872
            }
873
            _ => panic!("Expected binary expression"),
874
        }
875
    }
876
877
    #[test]
878
    fn test_unary_expression() {
879
        let operand = Box::new(Expr::new(
880
            ExprKind::Literal(Literal::Bool(true)),
881
            Span::new(1, 5),
882
        ));
883
        let expr = Expr::new(
884
            ExprKind::Unary {
885
                op: UnaryOp::Not,
886
                operand,
887
            },
888
            Span::new(0, 5),
889
        );
890
891
        match expr.kind {
892
            ExprKind::Unary { op, operand } => {
893
                assert_eq!(op, UnaryOp::Not);
894
                match operand.kind {
895
                    ExprKind::Literal(Literal::Bool(b)) => assert!(b),
896
                    _ => panic!("Wrong operand"),
897
                }
898
            }
899
            _ => panic!("Expected unary expression"),
900
        }
901
    }
902
903
    #[test]
904
    fn test_if_expression() {
905
        let condition = Box::new(Expr::new(
906
            ExprKind::Literal(Literal::Bool(true)),
907
            Span::new(3, 7),
908
        ));
909
        let then_branch = Box::new(Expr::new(
910
            ExprKind::Literal(Literal::Integer(1)),
911
            Span::new(10, 11),
912
        ));
913
        let else_branch = Some(Box::new(Expr::new(
914
            ExprKind::Literal(Literal::Integer(2)),
915
            Span::new(17, 18),
916
        )));
917
918
        let expr = Expr::new(
919
            ExprKind::If {
920
                condition,
921
                then_branch,
922
                else_branch,
923
            },
924
            Span::new(0, 18),
925
        );
926
927
        match expr.kind {
928
            ExprKind::If {
929
                condition: c,
930
                then_branch: t,
931
                else_branch: e,
932
            } => {
933
                match c.kind {
934
                    ExprKind::Literal(Literal::Bool(b)) => assert!(b),
935
                    _ => panic!("Wrong condition"),
936
                }
937
                match t.kind {
938
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 1),
939
                    _ => panic!("Wrong then branch"),
940
                }
941
                assert!(e.is_some());
942
                if let Some(else_expr) = e {
943
                    match else_expr.kind {
944
                        ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 2),
945
                        _ => panic!("Wrong else branch"),
946
                    }
947
                }
948
            }
949
            _ => panic!("Expected if expression"),
950
        }
951
    }
952
953
    #[test]
954
    fn test_let_expression() {
955
        let value = Box::new(Expr::new(
956
            ExprKind::Literal(Literal::Integer(42)),
957
            Span::new(8, 10),
958
        ));
959
        let body = Box::new(Expr::new(
960
            ExprKind::Identifier("x".to_string()),
961
            Span::new(14, 15),
962
        ));
963
964
        let expr = Expr::new(
965
            ExprKind::Let {
966
                name: "x".to_string(),
967
                type_annotation: None,
968
                value,
969
                body,
970
                is_mutable: false,
971
            },
972
            Span::new(0, 15),
973
        );
974
975
        match expr.kind {
976
            ExprKind::Let {
977
                name,
978
                value: v,
979
                body: b,
980
                ..
981
            } => {
982
                assert_eq!(name, "x");
983
                match v.kind {
984
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 42),
985
                    _ => panic!("Wrong value"),
986
                }
987
                match b.kind {
988
                    ExprKind::Identifier(id) => assert_eq!(id, "x"),
989
                    _ => panic!("Wrong body"),
990
                }
991
            }
992
            _ => panic!("Expected let expression"),
993
        }
994
    }
995
996
    #[test]
997
    fn test_function_expression() {
998
        let params = vec![Param {
999
            pattern: Pattern::Identifier("x".to_string()),
1000
            ty: Type {
1001
                kind: TypeKind::Named("i32".to_string()),
1002
                span: Span::new(10, 13),
1003
            },
1004
            span: Span::new(8, 13),
1005
            is_mutable: false,
1006
            default_value: None,
1007
        }];
1008
        let body = Box::new(Expr::new(
1009
            ExprKind::Identifier("x".to_string()),
1010
            Span::new(20, 21),
1011
        ));
1012
1013
        let expr = Expr::new(
1014
            ExprKind::Function {
1015
                name: "identity".to_string(),
1016
                type_params: vec![],
1017
                params,
1018
                return_type: Some(Type {
1019
                    kind: TypeKind::Named("i32".to_string()),
1020
                    span: Span::new(16, 19),
1021
                }),
1022
                body,
1023
                is_async: false,
1024
                is_pub: false,
1025
            },
1026
            Span::new(0, 22),
1027
        );
1028
1029
        match expr.kind {
1030
            ExprKind::Function {
1031
                name,
1032
                params: p,
1033
                return_type,
1034
                body: b,
1035
                ..
1036
            } => {
1037
                assert_eq!(name, "identity");
1038
                assert_eq!(p.len(), 1);
1039
                assert_eq!(p[0].name(), "x");
1040
                assert!(return_type.is_some());
1041
                match b.kind {
1042
                    ExprKind::Identifier(id) => assert_eq!(id, "x"),
1043
                    _ => panic!("Wrong body"),
1044
                }
1045
            }
1046
            _ => panic!("Expected function expression"),
1047
        }
1048
    }
1049
1050
    #[test]
1051
    fn test_call_expression() {
1052
        let func = Box::new(Expr::new(
1053
            ExprKind::Identifier("add".to_string()),
1054
            Span::new(0, 3),
1055
        ));
1056
        let args = vec![
1057
            Expr::new(ExprKind::Literal(Literal::Integer(1)), Span::new(4, 5)),
1058
            Expr::new(ExprKind::Literal(Literal::Integer(2)), Span::new(7, 8)),
1059
        ];
1060
1061
        let expr = Expr::new(ExprKind::Call { func, args }, Span::new(0, 9));
1062
1063
        match expr.kind {
1064
            ExprKind::Call { func: f, args: a } => {
1065
                match f.kind {
1066
                    ExprKind::Identifier(name) => assert_eq!(name, "add"),
1067
                    _ => panic!("Wrong function"),
1068
                }
1069
                assert_eq!(a.len(), 2);
1070
            }
1071
            _ => panic!("Expected call expression"),
1072
        }
1073
    }
1074
1075
    #[test]
1076
    fn test_block_expression() {
1077
        let exprs = vec![
1078
            Expr::new(ExprKind::Literal(Literal::Integer(1)), Span::new(2, 3)),
1079
            Expr::new(ExprKind::Literal(Literal::Integer(2)), Span::new(5, 6)),
1080
        ];
1081
1082
        let expr = Expr::new(ExprKind::Block(exprs), Span::new(0, 8));
1083
1084
        match expr.kind {
1085
            ExprKind::Block(block) => {
1086
                assert_eq!(block.len(), 2);
1087
            }
1088
            _ => panic!("Expected block expression"),
1089
        }
1090
    }
1091
1092
    #[test]
1093
    fn test_list_expression() {
1094
        let items = vec![
1095
            Expr::new(ExprKind::Literal(Literal::Integer(1)), Span::new(1, 2)),
1096
            Expr::new(ExprKind::Literal(Literal::Integer(2)), Span::new(4, 5)),
1097
            Expr::new(ExprKind::Literal(Literal::Integer(3)), Span::new(7, 8)),
1098
        ];
1099
1100
        let expr = Expr::new(ExprKind::List(items), Span::new(0, 9));
1101
1102
        match expr.kind {
1103
            ExprKind::List(list) => {
1104
                assert_eq!(list.len(), 3);
1105
            }
1106
            _ => panic!("Expected list expression"),
1107
        }
1108
    }
1109
1110
    #[test]
1111
    fn test_for_expression() {
1112
        let iter = Box::new(Expr::new(
1113
            ExprKind::Range {
1114
                start: Box::new(Expr::new(
1115
                    ExprKind::Literal(Literal::Integer(0)),
1116
                    Span::new(10, 11),
1117
                )),
1118
                end: Box::new(Expr::new(
1119
                    ExprKind::Literal(Literal::Integer(10)),
1120
                    Span::new(13, 15),
1121
                )),
1122
                inclusive: false,
1123
            },
1124
            Span::new(10, 15),
1125
        ));
1126
        let body = Box::new(Expr::new(
1127
            ExprKind::Identifier("i".to_string()),
1128
            Span::new(20, 21),
1129
        ));
1130
1131
        let expr = Expr::new(
1132
            ExprKind::For {
1133
                var: "i".to_string(),
1134
                pattern: None,
1135
                iter,
1136
                body,
1137
            },
1138
            Span::new(0, 22),
1139
        );
1140
1141
        match expr.kind {
1142
            ExprKind::For {
1143
                var,
1144
                iter: it,
1145
                body: b,
1146
                ..
1147
            } => {
1148
                assert_eq!(var, "i");
1149
                match it.kind {
1150
                    ExprKind::Range { .. } => {}
1151
                    _ => panic!("Wrong iterator"),
1152
                }
1153
                match b.kind {
1154
                    ExprKind::Identifier(id) => assert_eq!(id, "i"),
1155
                    _ => panic!("Wrong body"),
1156
                }
1157
            }
1158
            _ => panic!("Expected for expression"),
1159
        }
1160
    }
1161
1162
    #[test]
1163
    fn test_range_expression() {
1164
        let start = Box::new(Expr::new(
1165
            ExprKind::Literal(Literal::Integer(1)),
1166
            Span::new(0, 1),
1167
        ));
1168
        let end = Box::new(Expr::new(
1169
            ExprKind::Literal(Literal::Integer(10)),
1170
            Span::new(3, 5),
1171
        ));
1172
1173
        let expr = Expr::new(
1174
            ExprKind::Range {
1175
                start,
1176
                end,
1177
                inclusive: false,
1178
            },
1179
            Span::new(0, 5),
1180
        );
1181
1182
        match expr.kind {
1183
            ExprKind::Range {
1184
                start: s,
1185
                end: e,
1186
                inclusive,
1187
            } => {
1188
                assert!(!inclusive);
1189
                match s.kind {
1190
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 1),
1191
                    _ => panic!("Wrong start"),
1192
                }
1193
                match e.kind {
1194
                    ExprKind::Literal(Literal::Integer(n)) => assert_eq!(n, 10),
1195
                    _ => panic!("Wrong end"),
1196
                }
1197
            }
1198
            _ => panic!("Expected range expression"),
1199
        }
1200
    }
1201
1202
    #[test]
1203
    fn test_import_expression() {
1204
        let expr = Expr::new(
1205
            ExprKind::Import {
1206
                path: "std::collections".to_string(),
1207
                items: vec![
1208
                    ImportItem::Named("HashMap".to_string()),
1209
                    ImportItem::Named("HashSet".to_string()),
1210
                ],
1211
            },
1212
            Span::new(0, 30),
1213
        );
1214
1215
        match expr.kind {
1216
            ExprKind::Import { path, items } => {
1217
                assert_eq!(path, "std::collections");
1218
                assert_eq!(items.len(), 2);
1219
                assert_eq!(items[0], ImportItem::Named("HashMap".to_string()));
1220
                assert_eq!(items[1], ImportItem::Named("HashSet".to_string()));
1221
            }
1222
            _ => panic!("Expected import expression"),
1223
        }
1224
    }
1225
1226
    #[test]
1227
    fn test_pipeline_expression() {
1228
        let expr_start = Box::new(Expr::new(
1229
            ExprKind::List(vec![
1230
                Expr::new(ExprKind::Literal(Literal::Integer(1)), Span::new(1, 2)),
1231
                Expr::new(ExprKind::Literal(Literal::Integer(2)), Span::new(4, 5)),
1232
            ]),
1233
            Span::new(0, 6),
1234
        ));
1235
        let stages = vec![PipelineStage {
1236
            op: Box::new(Expr::new(
1237
                ExprKind::Identifier("filter".to_string()),
1238
                Span::new(10, 16),
1239
            )),
1240
            span: Span::new(10, 16),
1241
        }];
1242
1243
        let expr = Expr::new(
1244
            ExprKind::Pipeline {
1245
                expr: expr_start,
1246
                stages,
1247
            },
1248
            Span::new(0, 16),
1249
        );
1250
1251
        match expr.kind {
1252
            ExprKind::Pipeline { expr: e, stages: s } => {
1253
                assert_eq!(s.len(), 1);
1254
                match e.kind {
1255
                    ExprKind::List(list) => assert_eq!(list.len(), 2),
1256
                    _ => panic!("Wrong pipeline start"),
1257
                }
1258
            }
1259
            _ => panic!("Expected pipeline expression"),
1260
        }
1261
    }
1262
1263
    #[test]
1264
    fn test_match_expression() {
1265
        let expr_to_match = Box::new(Expr::new(
1266
            ExprKind::Identifier("x".to_string()),
1267
            Span::new(6, 7),
1268
        ));
1269
        let arms = vec![
1270
            MatchArm {
1271
                pattern: Pattern::Literal(Literal::Integer(1)),
1272
                guard: None,
1273
                body: Box::new(Expr::new(
1274
                    ExprKind::Literal(Literal::String("one".to_string())),
1275
                    Span::new(15, 20),
1276
                )),
1277
                span: Span::new(10, 20),
1278
            },
1279
            MatchArm {
1280
                pattern: Pattern::Wildcard,
1281
                guard: None,
1282
                body: Box::new(Expr::new(
1283
                    ExprKind::Literal(Literal::String("other".to_string())),
1284
                    Span::new(28, 35),
1285
                )),
1286
                span: Span::new(25, 35),
1287
            },
1288
        ];
1289
1290
        let expr = Expr::new(
1291
            ExprKind::Match {
1292
                expr: expr_to_match,
1293
                arms,
1294
            },
1295
            Span::new(0, 36),
1296
        );
1297
1298
        match expr.kind {
1299
            ExprKind::Match { expr: e, arms: a } => {
1300
                assert_eq!(a.len(), 2);
1301
                match e.kind {
1302
                    ExprKind::Identifier(id) => assert_eq!(id, "x"),
1303
                    _ => panic!("Wrong match expression"),
1304
                }
1305
            }
1306
            _ => panic!("Expected match expression"),
1307
        }
1308
    }
1309
1310
    #[test]
1311
    fn test_pattern_variants() {
1312
        let patterns = vec![
1313
            Pattern::Wildcard,
1314
            Pattern::Literal(Literal::Integer(42)),
1315
            Pattern::Identifier("x".to_string()),
1316
            Pattern::Tuple(vec![
1317
                Pattern::Literal(Literal::Integer(1)),
1318
                Pattern::Identifier("x".to_string()),
1319
            ]),
1320
            Pattern::List(vec![
1321
                Pattern::Literal(Literal::Integer(1)),
1322
                Pattern::Literal(Literal::Integer(2)),
1323
            ]),
1324
            Pattern::Struct {
1325
                name: "Point".to_string(),
1326
                fields: vec![StructPatternField {
1327
                    name: "x".to_string(),
1328
                    pattern: Some(Pattern::Identifier("x".to_string())),
1329
                }],
1330
                has_rest: false,
1331
            },
1332
            Pattern::Range {
1333
                start: Box::new(Pattern::Literal(Literal::Integer(1))),
1334
                end: Box::new(Pattern::Literal(Literal::Integer(10))),
1335
                inclusive: true,
1336
            },
1337
            Pattern::Or(vec![
1338
                Pattern::Literal(Literal::Integer(1)),
1339
                Pattern::Literal(Literal::Integer(2)),
1340
            ]),
1341
            Pattern::Rest,
1342
        ];
1343
1344
        for pattern in patterns {
1345
            match pattern {
1346
                Pattern::Tuple(list) | Pattern::List(list) => assert!(!list.is_empty()),
1347
                Pattern::Struct { fields, .. } => assert!(!fields.is_empty()),
1348
                Pattern::Or(patterns) => assert!(!patterns.is_empty()),
1349
                Pattern::Range { .. }
1350
                | Pattern::Wildcard
1351
                | Pattern::Literal(_)
1352
                | Pattern::Identifier(_)
1353
                | Pattern::Rest
1354
                | Pattern::RestNamed(_)
1355
                | Pattern::Ok(_)
1356
                | Pattern::Err(_)
1357
                | Pattern::Some(_)
1358
                | Pattern::None
1359
                | Pattern::QualifiedName(_) 
1360
                | Pattern::WithDefault { .. } => {} // Simple patterns
1361
            }
1362
        }
1363
    }
1364
1365
    #[test]
1366
    fn test_type_kinds() {
1367
        let types = vec![
1368
            Type {
1369
                kind: TypeKind::Named("i32".to_string()),
1370
                span: Span::new(0, 3),
1371
            },
1372
            Type {
1373
                kind: TypeKind::Optional(Box::new(Type {
1374
                    kind: TypeKind::Named("String".to_string()),
1375
                    span: Span::new(0, 6),
1376
                })),
1377
                span: Span::new(0, 7),
1378
            },
1379
            Type {
1380
                kind: TypeKind::List(Box::new(Type {
1381
                    kind: TypeKind::Named("f64".to_string()),
1382
                    span: Span::new(1, 4),
1383
                })),
1384
                span: Span::new(0, 5),
1385
            },
1386
            Type {
1387
                kind: TypeKind::Function {
1388
                    params: vec![Type {
1389
                        kind: TypeKind::Named("i32".to_string()),
1390
                        span: Span::new(0, 3),
1391
                    }],
1392
                    ret: Box::new(Type {
1393
                        kind: TypeKind::Named("String".to_string()),
1394
                        span: Span::new(7, 13),
1395
                    }),
1396
                },
1397
                span: Span::new(0, 13),
1398
            },
1399
        ];
1400
1401
        for ty in types {
1402
            match ty.kind {
1403
                TypeKind::Named(name) => assert!(!name.is_empty()),
1404
                TypeKind::Generic { base, params } => {
1405
                    assert!(!base.is_empty());
1406
                    assert!(!params.is_empty());
1407
                }
1408
                TypeKind::Optional(_) | TypeKind::List(_) | TypeKind::Series { .. } => {}
1409
                TypeKind::Function { params, .. } => assert!(!params.is_empty()),
1410
                TypeKind::DataFrame { columns } => assert!(!columns.is_empty()),
1411
                TypeKind::Tuple(ref types) => assert!(!types.is_empty()),
1412
                TypeKind::Reference { is_mut: _, ref inner } => {
1413
                    // Reference types should have a valid inner type
1414
                    if let TypeKind::Named(ref name) = inner.kind { 
1415
                        assert!(!name.is_empty());
1416
                    }
1417
                }
1418
            }
1419
        }
1420
    }
1421
1422
    #[test]
1423
    fn test_param_creation() {
1424
        let param = Param {
1425
            pattern: Pattern::Identifier("count".to_string()),
1426
            ty: Type {
1427
                kind: TypeKind::Named("usize".to_string()),
1428
                span: Span::new(6, 11),
1429
            },
1430
            span: Span::new(0, 11),
1431
            is_mutable: false,
1432
            default_value: None,
1433
        };
1434
1435
        assert_eq!(param.name(), "count");
1436
        match param.ty.kind {
1437
            TypeKind::Named(name) => assert_eq!(name, "usize"),
1438
            _ => panic!("Wrong type kind"),
1439
        }
1440
    }
1441
}