## 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/runtime/pattern_matching.rs

    Line
    Count
    Source
    1
    //! Shared pattern matching utilities
    2
    //! Extracted to reduce duplication across interpreter and REPL
    3
    4
    use crate::frontend::ast::{Literal, Pattern};
    5
    use crate::runtime::Value;
    6
    7
    /// Match a literal pattern against a value
    8
    ///
    9
    /// # Examples
    10
    ///
    11
    /// ```
    12
    /// use ruchy::runtime::pattern_matching::match_literal_pattern;
    13
    /// use ruchy::runtime::Value;
    14
    /// use ruchy::frontend::ast::Literal;
    15
    ///
    16
    /// let value = Value::Int(42);
    17
    /// let pattern = Literal::Integer(42);
    18
    /// assert!(match_literal_pattern(&value, &pattern));
    19
    ///
    20
    /// let pattern2 = Literal::Integer(43);
    21
    /// assert!(!match_literal_pattern(&value, &pattern2));
    22
    /// ```
    23
    0
    pub fn match_literal_pattern(value: &Value, literal: &Literal) -> bool {
    24
    0
        match (value, literal) {
    25
    0
            (Value::Unit, Literal::Unit) => true,
    26
    0
            (Value::Int(v), Literal::Integer(p)) => v == p,
    27
    0
            (Value::Float(v), Literal::Float(p)) => (v - p).abs() < f64::EPSILON,
    28
    0
            (Value::String(v), Literal::String(p)) => v == p,
    29
    0
            (Value::Bool(v), Literal::Bool(p)) => v == p,
    30
    0
            (Value::Char(v), Literal::Char(p)) => v == p,
    31
    0
            _ => false,
    32
        }
    33
    0
    }
    34
    35
    /// Helper function to match collection patterns (tuple or list)
    36
    0
    fn match_collection_patterns(patterns: &[Pattern], values: &[Value]) -> Option<Vec<(String, Value)>> {
    37
    0
        if patterns.len() != values.len() {
    38
    0
            return None;
    39
    0
        }
    40
    41
    0
        let mut bindings = Vec::new();
    42
    0
        for (pat, val) in patterns.iter().zip(values.iter()) {
    43
    0
            let sub_bindings = match_pattern(pat, val)?;
    44
    0
            bindings.extend(sub_bindings);
    45
        }
    46
    0
        Some(bindings)
    47
    0
    }
    48
    49
    /// Match a pattern against a value, returning bindings if successful
    50
    0
    pub fn match_pattern(pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
    51
    0
        match pattern {
    52
    0
            Pattern::Wildcard => Some(vec![]),
    53
    0
            Pattern::Identifier(name) => Some(vec![(name.clone(), value.clone())]),
    54
    0
            Pattern::Literal(lit) => match_literal_pattern_helper(value, lit),
    55
    0
            Pattern::Tuple(patterns) => match_tuple_pattern_helper(patterns, value),
    56
    0
            Pattern::List(patterns) => match_list_pattern_helper(patterns, value),
    57
    0
            Pattern::Struct { fields, .. } => match_struct_pattern_helper(fields, value),
    58
    0
            Pattern::Or(patterns) => match_or_pattern_helper(patterns, value),
    59
    0
            Pattern::Rest => Some(vec![]),
    60
    0
            Pattern::RestNamed(name) => Some(vec![(name.clone(), value.clone())]),
    61
    0
            Pattern::Range { start, end, inclusive } => match_range_pattern_helper(start, end, *inclusive, value),
    62
    0
            Pattern::QualifiedName(_) => None,
    63
    0
            Pattern::Some(inner_pattern) => match_some_pattern_helper(inner_pattern, value),
    64
    0
            Pattern::None => match_none_pattern_helper(value),
    65
    0
            Pattern::Ok(_) | Pattern::Err(_) => None,
    66
    0
            Pattern::WithDefault { pattern, .. } => {
    67
                // For default patterns, we match the inner pattern
    68
                // Default handling is done at the destructuring level
    69
    0
                match_pattern(pattern, value)
    70
            }
    71
        }
    72
    0
    }
    73
    74
    /// Check if two values are equal (for pattern matching)
    75
    0
    pub fn values_equal(v1: &Value, v2: &Value) -> bool {
    76
    0
        match (v1, v2) {
    77
    0
            (Value::Unit, Value::Unit) => true,
    78
    0
            (Value::Int(a), Value::Int(b)) => a == b,
    79
    0
            (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
    80
    0
            (Value::String(a), Value::String(b)) => a == b,
    81
    0
            (Value::Bool(a), Value::Bool(b)) => a == b,
    82
    0
            (Value::Char(a), Value::Char(b)) => a == b,
    83
    0
            (Value::List(a), Value::List(b)) => {
    84
    0
                a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
    85
            }
    86
    0
            (Value::Tuple(a), Value::Tuple(b)) => {
    87
    0
                a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
    88
            }
    89
    0
            (Value::Object(f1), Value::Object(f2)) => {
    90
    0
                f1.len() == f2.len() &&
    91
    0
                f1.iter().all(|(k, v)| f2.get(k).is_some_and(|v2| values_equal(v, v2)))
    92
            }
    93
    0
            (Value::Range { start: s1, end: e1, inclusive: i1 },
    94
    0
             Value::Range { start: s2, end: e2, inclusive: i2 }) => {
    95
    0
                s1 == s2 && e1 == e2 && i1 == i2
    96
            }
    97
    0
            _ => false,
    98
        }
    99
    0
    }
    100
    101
    /// Helper for matching literal patterns (complexity: 2)
    102
    0
    fn match_literal_pattern_helper(value: &Value, lit: &Literal) -> Option<Vec<(String, Value)>> {
    103
    0
        if match_literal_pattern(value, lit) {
    104
    0
            Some(vec![])
    105
        } else {
    106
    0
            None
    107
        }
    108
    0
    }
    109
    110
    /// Helper for matching tuple patterns (complexity: 3)
    111
    0
    fn match_tuple_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
    112
    0
        if let Value::Tuple(values) = value {
    113
    0
            match_collection_patterns(patterns, values)
    114
        } else {
    115
    0
            None
    116
        }
    117
    0
    }
    118
    119
    /// Helper for matching list patterns (complexity: 3)
    120
    0
    fn match_list_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
    121
    0
        if let Value::List(values) = value {
    122
    0
            match_collection_patterns(patterns, values)
    123
        } else {
    124
    0
            None
    125
        }
    126
    0
    }
    127
    128
    /// Helper for matching struct patterns (complexity: 8)
    129
    0
    fn match_struct_pattern_helper(fields: &[crate::frontend::ast::StructPatternField], value: &Value) -> Option<Vec<(String, Value)>> {
    130
    0
        if let Value::Object(obj_fields) = value {
    131
    0
            let mut bindings = Vec::new();
    132
    133
    0
            for field in fields {
    134
    0
                let field_value = obj_fields.get(&field.name)?;
    135
    136
    0
                if let Some(ref field_pattern) = field.pattern {
    137
    0
                    let field_bindings = match_pattern(field_pattern, field_value)?;
    138
    0
                    bindings.extend(field_bindings);
    139
    0
                } else {
    140
    0
                    bindings.push((field.name.clone(), field_value.clone()));
    141
    0
                }
    142
            }
    143
    144
    0
            Some(bindings)
    145
        } else {
    146
    0
            None
    147
        }
    148
    0
    }
    149
    150
    /// Helper for matching or patterns (complexity: 4)
    151
    0
    fn match_or_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
    152
    0
        for pat in patterns {
    153
    0
            if let Some(bindings) = match_pattern(pat, value) {
    154
    0
                return Some(bindings);
    155
    0
            }
    156
        }
    157
    0
        None
    158
    0
    }
    159
    160
    /// Helper for matching range patterns (complexity: 9)
    161
    0
    fn match_range_pattern_helper(start: &Pattern, end: &Pattern, inclusive: bool, value: &Value) -> Option<Vec<(String, Value)>> {
    162
    0
        if let Value::Int(val) = value {
    163
    0
            let start_val = if let Pattern::Literal(Literal::Integer(n)) = start {
    164
    0
                *n
    165
            } else {
    166
    0
                return None;
    167
            };
    168
    169
    0
            let end_val = if let Pattern::Literal(Literal::Integer(n)) = end {
    170
    0
                *n
    171
            } else {
    172
    0
                return None;
    173
            };
    174
    175
    0
            let val = *val;
    176
    0
            let in_range = if inclusive {
    177
    0
                val >= start_val && val <= end_val
    178
            } else {
    179
    0
                val >= start_val && val < end_val
    180
            };
    181
    182
    0
            if in_range {
    183
    0
                Some(Vec::new())
    184
            } else {
    185
    0
                None
    186
            }
    187
        } else {
    188
    0
            None
    189
        }
    190
    0
    }
    191
    192
    /// Helper for matching Some patterns (complexity: 6)
    193
    0
    fn match_some_pattern_helper(inner_pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
    194
    0
        if let Value::EnumVariant { variant_name, data, .. } = value {
    195
    0
            if variant_name == "Some" {
    196
    0
                if let Some(ref variant_data) = data {
    197
    0
                    if !variant_data.is_empty() {
    198
    0
                        return match_pattern(inner_pattern, &variant_data[0]);
    199
    0
                    }
    200
    0
                }
    201
    0
            }
    202
    0
        }
    203
    0
        None
    204
    0
    }
    205
    206
    /// Helper for matching None patterns (complexity: 4)
    207
    0
    fn match_none_pattern_helper(value: &Value) -> Option<Vec<(String, Value)>> {
    208
    0
        if let Value::EnumVariant { variant_name, .. } = value {
    209
    0
            if variant_name == "None" {
    210
    0
                return Some(Vec::new());
    211
    0
            }
    212
    0
        }
    213
    0
        None
    214
    0
    }