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
}