use crate::frontend::ast::{Literal, Pattern};
use crate::runtime::Value;
use std::collections::HashMap;
#[cfg(test)]
use std::sync::Arc;
pub fn match_literal_pattern(value: &Value, literal: &Literal) -> bool {
match (value, literal) {
(Value::Nil, Literal::Unit) => true,
(Value::Integer(v), Literal::Integer(p, _)) => v == p,
(Value::Float(v), Literal::Float(p)) => (v - p).abs() < f64::EPSILON,
(Value::String(v), Literal::String(p)) => &**v == p,
(Value::Bool(v), Literal::Bool(p)) => v == p,
_ => false,
}
}
fn match_collection_patterns(
patterns: &[Pattern],
values: &[Value],
) -> Option<Vec<(String, Value)>> {
let rest_position = patterns
.iter()
.position(|p| matches!(p, Pattern::Rest | Pattern::RestNamed(_)));
if let Some(rest_idx) = rest_position {
match_patterns_with_rest(patterns, values, rest_idx)
} else {
match_patterns_without_rest(patterns, values)
}
}
fn match_patterns_with_rest(
patterns: &[Pattern],
values: &[Value],
rest_idx: usize,
) -> Option<Vec<(String, Value)>> {
let before_rest = &patterns[..rest_idx];
let after_rest = &patterns[rest_idx + 1..];
if values.len() < before_rest.len() + after_rest.len() {
return None;
}
let mut bindings = Vec::new();
bindings.extend(match_pattern_sequence(
before_rest,
&values[..before_rest.len()],
)?);
let rest_start = before_rest.len();
let rest_end = values.len() - after_rest.len();
let rest_values = &values[rest_start..rest_end];
if let Pattern::RestNamed(name) = &patterns[rest_idx] {
bindings.push((name.clone(), Value::from_array(rest_values.to_vec())));
}
bindings.extend(match_pattern_sequence(after_rest, &values[rest_end..])?);
Some(bindings)
}
fn match_patterns_without_rest(
patterns: &[Pattern],
values: &[Value],
) -> Option<Vec<(String, Value)>> {
if patterns.len() != values.len() {
return None;
}
match_pattern_sequence(patterns, values)
}
fn match_pattern_sequence(patterns: &[Pattern], values: &[Value]) -> Option<Vec<(String, Value)>> {
let mut bindings = Vec::new();
for (pat, val) in patterns.iter().zip(values.iter()) {
let sub_bindings = match_pattern(pat, val)?;
bindings.extend(sub_bindings);
}
Some(bindings)
}
pub fn match_pattern(pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
match pattern {
Pattern::Wildcard => Some(vec![]),
Pattern::Identifier(name) => Some(vec![(name.clone(), value.clone())]),
Pattern::Literal(lit) => match_literal_pattern_helper(value, lit),
Pattern::Tuple(patterns) => match_tuple_pattern_helper(patterns, value),
Pattern::List(patterns) => match_list_pattern_helper(patterns, value),
Pattern::Struct { fields, .. } => match_struct_pattern_helper(fields, value),
Pattern::Or(patterns) => match_or_pattern_helper(patterns, value),
Pattern::Rest | Pattern::RestNamed(_) => {
Some(vec![])
}
Pattern::AtBinding { name, pattern } => {
if let Some(mut bindings) = match_pattern(pattern, value) {
bindings.push((name.clone(), value.clone()));
Some(bindings)
} else {
None
}
}
Pattern::Range {
start,
end,
inclusive,
} => match_range_pattern_helper(start, end, *inclusive, value),
Pattern::QualifiedName(_) => None,
Pattern::TupleVariant { path: _, patterns } => {
match_tuple_pattern_helper(patterns, value)
}
Pattern::Some(inner_pattern) => match_some_pattern_helper(inner_pattern, value),
Pattern::None => match_none_pattern_helper(value),
Pattern::Ok(inner_pattern) => match_ok_pattern_helper(inner_pattern, value),
Pattern::Err(inner_pattern) => match_err_pattern_helper(inner_pattern, value),
Pattern::WithDefault { pattern, .. } => {
match_pattern(pattern, value)
}
Pattern::Mut(inner_pattern) => {
match_pattern(inner_pattern, value)
}
}
}
pub fn values_equal(v1: &Value, v2: &Value) -> bool {
match (v1, v2) {
(Value::Nil, Value::Nil) => true,
(Value::Integer(a), Value::Integer(b)) => a == b,
(Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON,
(Value::String(a), Value::String(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Array(a), Value::Array(b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
}
(Value::Tuple(a), Value::Tuple(b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))
}
(Value::Object(f1), Value::Object(f2)) => {
f1.len() == f2.len()
&& f1
.iter()
.all(|(k, v)| f2.get(k).is_some_and(|v2| values_equal(v, v2)))
}
(
Value::Range {
start: s1,
end: e1,
inclusive: i1,
},
Value::Range {
start: s2,
end: e2,
inclusive: i2,
},
) => values_equal(s1, s2) && values_equal(e1, e2) && i1 == i2,
(
Value::EnumVariant {
variant_name: n1,
data: d1,
..
},
Value::EnumVariant {
variant_name: n2,
data: d2,
..
},
) => {
n1 == n2
&& match (d1, d2) {
(Some(v1), Some(v2)) => {
v1.len() == v2.len()
&& v1.iter().zip(v2.iter()).all(|(x, y)| values_equal(x, y))
}
(None, None) => true,
_ => false,
}
}
_ => false,
}
}
fn match_literal_pattern_helper(value: &Value, lit: &Literal) -> Option<Vec<(String, Value)>> {
if match_literal_pattern(value, lit) {
Some(vec![])
} else {
None
}
}
fn match_tuple_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
if let Value::Tuple(values) = value {
match_collection_patterns(patterns, values)
} else {
None
}
}
fn match_list_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
if let Value::Array(values) = value {
match_collection_patterns(patterns, values)
} else {
None
}
}
fn match_struct_pattern_helper(
fields: &[crate::frontend::ast::StructPatternField],
value: &Value,
) -> Option<Vec<(String, Value)>> {
if let Value::Object(obj_fields) = value {
let mut bindings = Vec::new();
for field in fields {
let field_value = obj_fields.get(&field.name)?;
if let Some(ref field_pattern) = field.pattern {
let field_bindings = match_pattern(field_pattern, field_value)?;
bindings.extend(field_bindings);
} else {
bindings.push((field.name.clone(), field_value.clone()));
}
}
Some(bindings)
} else {
None
}
}
fn match_or_pattern_helper(patterns: &[Pattern], value: &Value) -> Option<Vec<(String, Value)>> {
for pat in patterns {
if let Some(bindings) = match_pattern(pat, value) {
return Some(bindings);
}
}
None
}
fn match_range_pattern_helper(
start: &Pattern,
end: &Pattern,
inclusive: bool,
value: &Value,
) -> Option<Vec<(String, Value)>> {
if let Value::Integer(val) = value {
let start_val = if let Pattern::Literal(Literal::Integer(n, _)) = start {
*n
} else {
return None;
};
let end_val = if let Pattern::Literal(Literal::Integer(n, _)) = end {
*n
} else {
return None;
};
let val = *val;
let in_range = if inclusive {
val >= start_val && val <= end_val
} else {
val >= start_val && val < end_val
};
if in_range {
Some(Vec::new())
} else {
None
}
} else {
None
}
}
fn match_some_pattern_helper(
inner_pattern: &Pattern,
value: &Value,
) -> Option<Vec<(String, Value)>> {
if let Value::EnumVariant {
variant_name, data, ..
} = value
{
if variant_name == "Some" {
if let Some(ref variant_data) = data {
if !variant_data.is_empty() {
return match_pattern(inner_pattern, &variant_data[0]);
}
}
}
}
None
}
fn match_none_pattern_helper(value: &Value) -> Option<Vec<(String, Value)>> {
if let Value::EnumVariant { variant_name, .. } = value {
if variant_name == "None" {
return Some(Vec::new());
}
}
None
}
fn match_ok_pattern_helper(inner_pattern: &Pattern, value: &Value) -> Option<Vec<(String, Value)>> {
let fields = extract_object_fields(value)?;
if !is_ok_type(fields) {
return None;
}
let data = extract_ok_data(fields)?;
if data.is_empty() {
return None;
}
match_pattern(inner_pattern, &data[0])
}
fn extract_object_fields(value: &Value) -> Option<&HashMap<String, Value>> {
if let Value::Object(fields) = value {
Some(fields)
} else {
None
}
}
fn is_ok_type(fields: &HashMap<String, Value>) -> bool {
if let Some(Value::String(type_str)) = fields.get("type") {
&**type_str == "Ok"
} else {
false
}
}
fn extract_ok_data(fields: &HashMap<String, Value>) -> Option<&[Value]> {
if let Some(Value::Array(data)) = fields.get("data") {
Some(data)
} else {
None
}
}
fn match_err_pattern_helper(
inner_pattern: &Pattern,
value: &Value,
) -> Option<Vec<(String, Value)>> {
if let Value::Object(fields) = value {
if let Some(Value::String(type_str)) = fields.get("type") {
if &**type_str == "Err" {
if let Some(Value::Array(data)) = fields.get("data") {
if !data.is_empty() {
return match_pattern(inner_pattern, &data[0]);
}
}
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::{Expr, ExprKind, Literal, Pattern, Span, StructPatternField};
use crate::runtime::Value;
use std::collections::HashMap;
fn create_test_values() -> Vec<Value> {
vec![
Value::Nil,
Value::Integer(42),
Value::Float(3.15),
Value::from_string("test".to_string()),
Value::Bool(true),
Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)])),
Value::Tuple(Arc::from(vec![
Value::from_string("hello".to_string()),
Value::Integer(10),
])),
]
}
#[test]
fn test_literal_pattern_matching() {
assert!(match_literal_pattern(
&Value::Integer(42),
&Literal::Integer(42, None)
));
assert!(!match_literal_pattern(
&Value::Integer(42),
&Literal::Integer(43, None)
));
assert!(match_literal_pattern(
&Value::Float(3.15),
&Literal::Float(3.15)
));
assert!(match_literal_pattern(
&Value::Float(1.0),
&Literal::Float(1.0 + f64::EPSILON / 2.0)
));
assert!(!match_literal_pattern(
&Value::Float(1.0),
&Literal::Float(1.5)
));
assert!(match_literal_pattern(
&Value::from_string("hello".to_string()),
&Literal::String("hello".to_string())
));
assert!(!match_literal_pattern(
&Value::from_string("hello".to_string()),
&Literal::String("world".to_string())
));
assert!(match_literal_pattern(
&Value::Bool(true),
&Literal::Bool(true)
));
assert!(!match_literal_pattern(
&Value::Bool(true),
&Literal::Bool(false)
));
assert!(match_literal_pattern(&Value::Nil, &Literal::Unit));
assert!(!match_literal_pattern(
&Value::Integer(42),
&Literal::String("42".to_string())
));
assert!(!match_literal_pattern(
&Value::from_string("true".to_string()),
&Literal::Bool(true)
));
}
#[test]
fn test_values_equal() {
assert!(values_equal(&Value::Integer(42), &Value::Integer(42)));
assert!(!values_equal(&Value::Integer(42), &Value::Integer(43)));
assert!(values_equal(&Value::Float(3.15), &Value::Float(3.15)));
assert!(values_equal(
&Value::Float(1.0),
&Value::Float(1.0 + f64::EPSILON / 2.0)
));
assert!(!values_equal(&Value::Float(1.0), &Value::Float(1.5)));
assert!(values_equal(
&Value::from_string("test".to_string()),
&Value::from_string("test".to_string())
));
assert!(!values_equal(
&Value::from_string("test".to_string()),
&Value::from_string("other".to_string())
));
let list1 = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::from_string("test".to_string()),
]));
let list2 = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::from_string("test".to_string()),
]));
let list3 = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::from_string("other".to_string()),
]));
assert!(values_equal(&list1, &list2));
assert!(!values_equal(&list1, &list3));
let tuple1 = Value::Tuple(Arc::from(vec![Value::Bool(true), Value::Integer(42)]));
let tuple2 = Value::Tuple(Arc::from(vec![Value::Bool(true), Value::Integer(42)]));
let tuple3 = Value::Tuple(Arc::from(vec![Value::Bool(false), Value::Integer(42)]));
assert!(values_equal(&tuple1, &tuple2));
assert!(!values_equal(&tuple1, &tuple3));
let short_list = Value::Array(Arc::from(vec![Value::Integer(1)]));
let long_list = Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
assert!(!values_equal(&short_list, &long_list));
assert!(!values_equal(
&Value::Integer(42),
&Value::from_string("42".to_string())
));
assert!(!values_equal(
&Value::from_array(vec![]),
&Value::Tuple(Arc::from(vec![]))
));
}
#[test]
fn test_simple_pattern_matching() {
assert!(match_pattern(&Pattern::Wildcard, &Value::Integer(42)).is_some());
assert!(
match_pattern(&Pattern::Wildcard, &Value::from_string("test".to_string())).is_some()
);
assert!(match_pattern(&Pattern::Wildcard, &Value::Nil).is_some());
let binding = match_pattern(&Pattern::Identifier("x".to_string()), &Value::Integer(42));
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "x");
assert!(values_equal(&bindings[0].1, &Value::Integer(42)));
let literal_pattern = Pattern::Literal(Literal::Integer(42, None));
assert!(match_pattern(&literal_pattern, &Value::Integer(42)).is_some());
assert!(match_pattern(&literal_pattern, &Value::Integer(43)).is_none());
}
#[test]
fn test_tuple_pattern_matching() {
let tuple_value = Value::Tuple(Arc::from(vec![
Value::Integer(1),
Value::from_string("test".to_string()),
Value::Bool(true),
]));
let tuple_pattern = Pattern::Tuple(vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Identifier("s".to_string()),
Pattern::Literal(Literal::Bool(true)),
]);
let binding = match_pattern(&tuple_pattern, &tuple_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "s");
assert!(values_equal(
&bindings[0].1,
&Value::from_string("test".to_string())
));
let wrong_pattern = Pattern::Tuple(vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Identifier("s".to_string()),
]);
assert!(match_pattern(&wrong_pattern, &tuple_value).is_none());
let mismatch_pattern = Pattern::Tuple(vec![
Pattern::Literal(Literal::Integer(2, None)),
Pattern::Identifier("s".to_string()),
Pattern::Literal(Literal::Bool(true)),
]);
assert!(match_pattern(&mismatch_pattern, &tuple_value).is_none());
}
#[test]
fn test_list_pattern_matching() {
let list_value = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
]));
let list_pattern = Pattern::List(vec![
Pattern::Identifier("first".to_string()),
Pattern::Literal(Literal::Integer(2, None)),
Pattern::Identifier("last".to_string()),
]);
let binding = match_pattern(&list_pattern, &list_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 2);
let first_binding = bindings
.iter()
.find(|(name, _)| name == "first")
.expect("operation should succeed in test");
let last_binding = bindings
.iter()
.find(|(name, _)| name == "last")
.expect("operation should succeed in test");
assert!(values_equal(&first_binding.1, &Value::Integer(1)));
assert!(values_equal(&last_binding.1, &Value::Integer(3)));
let empty_pattern = Pattern::List(vec![]);
assert!(match_pattern(&empty_pattern, &Value::from_array(vec![])).is_some());
assert!(match_pattern(&empty_pattern, &list_value).is_none());
let short_pattern = Pattern::List(vec![Pattern::Identifier("x".to_string())]);
assert!(match_pattern(&short_pattern, &list_value).is_none());
}
#[test]
fn test_rest_pattern_matching() {
let list_value = Value::Array(Arc::from(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4),
]));
let rest_pattern = Pattern::List(vec![
Pattern::Identifier("first".to_string()),
Pattern::Rest,
]);
let binding = match_pattern(&rest_pattern, &list_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "first");
assert!(values_equal(&bindings[0].1, &Value::Integer(1)));
let named_rest_pattern = Pattern::List(vec![
Pattern::Identifier("first".to_string()),
Pattern::RestNamed("middle".to_string()),
Pattern::Identifier("last".to_string()),
]);
let binding = match_pattern(&named_rest_pattern, &list_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 3);
let first_binding = bindings
.iter()
.find(|(name, _)| name == "first")
.expect("operation should succeed in test");
let middle_binding = bindings
.iter()
.find(|(name, _)| name == "middle")
.expect("operation should succeed in test");
let last_binding = bindings
.iter()
.find(|(name, _)| name == "last")
.expect("operation should succeed in test");
assert!(values_equal(&first_binding.1, &Value::Integer(1)));
assert!(values_equal(&last_binding.1, &Value::Integer(4)));
if let Value::Array(middle_values) = &middle_binding.1 {
assert_eq!(middle_values.len(), 2);
assert!(values_equal(&middle_values[0], &Value::Integer(2)));
assert!(values_equal(&middle_values[1], &Value::Integer(3)));
} else {
panic!("Expected middle binding to be a list");
}
}
#[test]
fn test_or_pattern_matching() {
let or_pattern = Pattern::Or(vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Literal(Literal::Integer(2, None)),
Pattern::Literal(Literal::String("test".to_string())),
]);
assert!(match_pattern(&or_pattern, &Value::Integer(1)).is_some());
assert!(match_pattern(&or_pattern, &Value::Integer(2)).is_some());
assert!(match_pattern(&or_pattern, &Value::from_string("test".to_string())).is_some());
assert!(match_pattern(&or_pattern, &Value::Integer(3)).is_none());
assert!(match_pattern(&or_pattern, &Value::from_string("other".to_string())).is_none());
}
#[test]
fn test_option_pattern_matching() {
let some_value = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![Value::Integer(42)]),
};
let some_pattern = Pattern::Some(Box::new(Pattern::Identifier("value".to_string())));
let binding = match_pattern(&some_pattern, &some_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "value");
assert!(values_equal(&bindings[0].1, &Value::Integer(42)));
let none_value = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "None".to_string(),
data: None,
};
let none_pattern = Pattern::None;
assert!(match_pattern(&none_pattern, &none_value).is_some());
assert!(match_pattern(&none_pattern, &some_value).is_none());
}
#[test]
fn test_struct_pattern_matching() {
let struct_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("name".to_string(), Value::from_string("Alice".to_string()));
map.insert("age".to_string(), Value::Integer(30));
map.insert("active".to_string(), Value::Bool(true));
map
}));
let struct_pattern = Pattern::Struct {
name: "Person".to_string(),
fields: vec![
StructPatternField {
name: "name".to_string(),
pattern: Some(Pattern::Identifier("person_name".to_string())),
},
StructPatternField {
name: "age".to_string(),
pattern: Some(Pattern::Identifier("person_age".to_string())),
},
],
has_rest: false,
};
let binding = match_pattern(&struct_pattern, &struct_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 2);
let name_binding = bindings
.iter()
.find(|(name, _)| name == "person_name")
.expect("operation should succeed in test");
let age_binding = bindings
.iter()
.find(|(name, _)| name == "person_age")
.expect("operation should succeed in test");
assert!(values_equal(
&name_binding.1,
&Value::from_string("Alice".to_string())
));
assert!(values_equal(&age_binding.1, &Value::Integer(30)));
}
#[test]
fn test_range_pattern_edge_cases() {
let range_value = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: true,
};
let matching_range = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: true,
};
let non_matching_range = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: false,
};
assert!(values_equal(&range_value, &matching_range));
assert!(!values_equal(&range_value, &non_matching_range));
assert!(match_pattern(&Pattern::Wildcard, &Value::from_array(vec![])).is_some());
assert!(match_pattern(&Pattern::List(vec![]), &Value::from_array(vec![])).is_some());
}
#[test]
fn test_nested_pattern_matching() {
let complex_value = Value::Tuple(Arc::from(vec![
Value::from_string("outer".to_string()),
Value::Array(Arc::from(vec![Value::Integer(1), Value::Integer(2)])),
Value::Tuple(Arc::from(vec![Value::Bool(true)])),
]));
let nested_pattern = Pattern::Tuple(vec![
Pattern::Identifier("outer_str".to_string()),
Pattern::List(vec![
Pattern::Identifier("first_int".to_string()),
Pattern::Identifier("second_int".to_string()),
]),
Pattern::Tuple(vec![Pattern::Literal(Literal::Bool(true))]),
]);
let binding = match_pattern(&nested_pattern, &complex_value);
assert!(binding.is_some());
let bindings = binding.expect("operation should succeed in test");
assert_eq!(bindings.len(), 3);
let outer_binding = bindings
.iter()
.find(|(name, _)| name == "outer_str")
.expect("operation should succeed in test");
let first_binding = bindings
.iter()
.find(|(name, _)| name == "first_int")
.expect("operation should succeed in test");
let second_binding = bindings
.iter()
.find(|(name, _)| name == "second_int")
.expect("operation should succeed in test");
assert!(values_equal(
&outer_binding.1,
&Value::from_string("outer".to_string())
));
assert!(values_equal(&first_binding.1, &Value::Integer(1)));
assert!(values_equal(&second_binding.1, &Value::Integer(2)));
}
#[test]
fn test_pattern_matching_failures() {
let tuple_pattern = Pattern::Tuple(vec![Pattern::Identifier("x".to_string())]);
assert!(match_pattern(&tuple_pattern, &Value::Integer(42)).is_none());
let list_pattern = Pattern::List(vec![Pattern::Identifier("x".to_string())]);
assert!(match_pattern(&list_pattern, &Value::from_string("test".to_string())).is_none());
let long_pattern = Pattern::Tuple(vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
Pattern::Identifier("c".to_string()),
]);
let short_tuple = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
assert!(match_pattern(&long_pattern, &short_tuple).is_none());
let literal_pattern = Pattern::Literal(Literal::Integer(42, None));
assert!(match_pattern(&literal_pattern, &Value::Integer(43)).is_none());
assert!(match_pattern(&literal_pattern, &Value::from_string("42".to_string())).is_none());
}
#[test]
fn test_at_binding_pattern() {
let at_pattern = Pattern::AtBinding {
name: "whole".to_string(),
pattern: Box::new(Pattern::Tuple(vec![
Pattern::Identifier("x".to_string()),
Pattern::Identifier("y".to_string()),
])),
};
let tuple = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let binding = match_pattern(&at_pattern, &tuple);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 3); assert!(bindings.iter().any(|(n, _)| n == "whole"));
assert!(bindings.iter().any(|(n, _)| n == "x"));
assert!(bindings.iter().any(|(n, _)| n == "y"));
}
#[test]
fn test_at_binding_pattern_failure() {
let at_pattern = Pattern::AtBinding {
name: "whole".to_string(),
pattern: Box::new(Pattern::Literal(Literal::Integer(42, None))),
};
let value = Value::Integer(99);
assert!(match_pattern(&at_pattern, &value).is_none());
}
#[test]
fn test_mut_pattern() {
let mut_pattern = Pattern::Mut(Box::new(Pattern::Identifier("x".to_string())));
let value = Value::Integer(42);
let binding = match_pattern(&mut_pattern, &value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "x");
assert!(values_equal(&bindings[0].1, &Value::Integer(42)));
}
#[test]
fn test_with_default_pattern() {
let default_pattern = Pattern::WithDefault {
pattern: Box::new(Pattern::Identifier("x".to_string())),
default: Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(0, None)),
Span::new(0, 1),
)),
};
let value = Value::Integer(42);
let binding = match_pattern(&default_pattern, &value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "x");
}
#[test]
fn test_qualified_name_pattern() {
let qn_pattern = Pattern::QualifiedName(vec!["Foo".to_string(), "Bar".to_string()]);
let value = Value::Integer(42);
assert!(match_pattern(&qn_pattern, &value).is_none());
}
#[test]
fn test_tuple_variant_pattern() {
let variant_pattern = Pattern::TupleVariant {
path: vec!["Some".to_string()],
patterns: vec![Pattern::Identifier("inner".to_string())],
};
let tuple_value = Value::Tuple(Arc::from(vec![Value::Integer(42)]));
let binding = match_pattern(&variant_pattern, &tuple_value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "inner");
}
#[test]
fn test_rest_pattern_standalone() {
let rest_pattern = Pattern::Rest;
let value = Value::Integer(42);
let binding = match_pattern(&rest_pattern, &value);
assert!(binding.is_some());
assert!(binding.expect("should succeed").is_empty());
}
#[test]
fn test_rest_named_pattern_standalone() {
let rest_named = Pattern::RestNamed("rest".to_string());
let value = Value::Integer(42);
let binding = match_pattern(&rest_named, &value);
assert!(binding.is_some());
assert!(binding.expect("should succeed").is_empty());
}
#[test]
fn test_ok_pattern_matching() {
let ok_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Ok".to_string()));
map.insert(
"data".to_string(),
Value::Array(Arc::from(vec![Value::Integer(42)])),
);
map
}));
let ok_pattern = Pattern::Ok(Box::new(Pattern::Identifier("val".to_string())));
let binding = match_pattern(&ok_pattern, &ok_value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "val");
assert!(values_equal(&bindings[0].1, &Value::Integer(42)));
}
#[test]
fn test_err_pattern_matching() {
let err_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Err".to_string()));
map.insert(
"data".to_string(),
Value::Array(Arc::from(vec![Value::from_string("error".to_string())])),
);
map
}));
let err_pattern = Pattern::Err(Box::new(Pattern::Identifier("e".to_string())));
let binding = match_pattern(&err_pattern, &err_value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "e");
}
#[test]
fn test_ok_pattern_not_matching_err() {
let err_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Err".to_string()));
map.insert(
"data".to_string(),
Value::Array(Arc::from(vec![Value::Integer(42)])),
);
map
}));
let ok_pattern = Pattern::Ok(Box::new(Pattern::Identifier("val".to_string())));
assert!(match_pattern(&ok_pattern, &err_value).is_none());
}
#[test]
fn test_err_pattern_not_matching_ok() {
let ok_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Ok".to_string()));
map.insert(
"data".to_string(),
Value::Array(Arc::from(vec![Value::Integer(42)])),
);
map
}));
let err_pattern = Pattern::Err(Box::new(Pattern::Identifier("e".to_string())));
assert!(match_pattern(&err_pattern, &ok_value).is_none());
}
#[test]
fn test_range_pattern_matching_inclusive() {
let range_pattern = Pattern::Range {
start: Box::new(Pattern::Literal(Literal::Integer(1, None))),
end: Box::new(Pattern::Literal(Literal::Integer(10, None))),
inclusive: true,
};
assert!(match_pattern(&range_pattern, &Value::Integer(1)).is_some());
assert!(match_pattern(&range_pattern, &Value::Integer(5)).is_some());
assert!(match_pattern(&range_pattern, &Value::Integer(10)).is_some());
assert!(match_pattern(&range_pattern, &Value::Integer(0)).is_none());
assert!(match_pattern(&range_pattern, &Value::Integer(11)).is_none());
}
#[test]
fn test_range_pattern_matching_exclusive() {
let range_pattern = Pattern::Range {
start: Box::new(Pattern::Literal(Literal::Integer(1, None))),
end: Box::new(Pattern::Literal(Literal::Integer(10, None))),
inclusive: false,
};
assert!(match_pattern(&range_pattern, &Value::Integer(1)).is_some());
assert!(match_pattern(&range_pattern, &Value::Integer(9)).is_some());
assert!(match_pattern(&range_pattern, &Value::Integer(10)).is_none()); assert!(match_pattern(&range_pattern, &Value::Integer(0)).is_none());
}
#[test]
fn test_enum_variant_equality() {
let v1 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![Value::Integer(42)]),
};
let v2 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![Value::Integer(42)]),
};
let v3 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![Value::Integer(99)]),
};
assert!(values_equal(&v1, &v2));
assert!(!values_equal(&v1, &v3));
}
#[test]
fn test_enum_variant_none_equality() {
let v1 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "None".to_string(),
data: None,
};
let v2 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "None".to_string(),
data: None,
};
let v3 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: None,
};
assert!(values_equal(&v1, &v2));
assert!(!values_equal(&v1, &v3)); }
#[test]
fn test_object_equality() {
let o1 = Value::Object(Arc::new({
let mut m = HashMap::new();
m.insert("a".to_string(), Value::Integer(1));
m.insert("b".to_string(), Value::Integer(2));
m
}));
let o2 = Value::Object(Arc::new({
let mut m = HashMap::new();
m.insert("a".to_string(), Value::Integer(1));
m.insert("b".to_string(), Value::Integer(2));
m
}));
let o3 = Value::Object(Arc::new({
let mut m = HashMap::new();
m.insert("a".to_string(), Value::Integer(1));
m
}));
assert!(values_equal(&o1, &o2));
assert!(!values_equal(&o1, &o3)); }
#[test]
fn test_nil_equality() {
assert!(values_equal(&Value::Nil, &Value::Nil));
assert!(!values_equal(&Value::Nil, &Value::Integer(0)));
}
#[test]
fn test_bool_equality() {
assert!(values_equal(&Value::Bool(true), &Value::Bool(true)));
assert!(values_equal(&Value::Bool(false), &Value::Bool(false)));
assert!(!values_equal(&Value::Bool(true), &Value::Bool(false)));
}
#[test]
fn test_some_pattern_on_wrong_type() {
let some_pattern = Pattern::Some(Box::new(Pattern::Identifier("x".to_string())));
assert!(match_pattern(&some_pattern, &Value::Integer(42)).is_none());
}
#[test]
fn test_none_pattern_on_wrong_type() {
let none_pattern = Pattern::None;
assert!(match_pattern(&none_pattern, &Value::Integer(42)).is_none());
}
#[test]
fn test_range_pattern_non_integer() {
let range_pattern = Pattern::Range {
start: Box::new(Pattern::Literal(Literal::Integer(1, None))),
end: Box::new(Pattern::Literal(Literal::Integer(10, None))),
inclusive: true,
};
assert!(match_pattern(&range_pattern, &Value::Float(5.0)).is_none());
}
#[test]
fn test_struct_pattern_missing_field() {
let struct_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("name".to_string(), Value::from_string("Alice".to_string()));
map
}));
let struct_pattern = Pattern::Struct {
name: "Person".to_string(),
fields: vec![
StructPatternField {
name: "name".to_string(),
pattern: Some(Pattern::Identifier("n".to_string())),
},
StructPatternField {
name: "age".to_string(), pattern: Some(Pattern::Identifier("a".to_string())),
},
],
has_rest: false,
};
assert!(match_pattern(&struct_pattern, &struct_value).is_none());
}
#[test]
fn test_struct_pattern_shorthand() {
let struct_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("name".to_string(), Value::from_string("Alice".to_string()));
map
}));
let struct_pattern = Pattern::Struct {
name: "Person".to_string(),
fields: vec![StructPatternField {
name: "name".to_string(),
pattern: None, }],
has_rest: false,
};
let binding = match_pattern(&struct_pattern, &struct_value);
assert!(binding.is_some());
let bindings = binding.expect("should succeed");
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "name");
}
#[test]
fn test_list_pattern_rest_not_enough_elements() {
let list_value = Value::Array(Arc::from(vec![Value::Integer(1)]));
let pattern = Pattern::List(vec![
Pattern::Identifier("first".to_string()),
Pattern::RestNamed("middle".to_string()),
Pattern::Identifier("last".to_string()),
]);
assert!(match_pattern(&pattern, &list_value).is_none());
}
#[test]
fn test_ok_pattern_empty_data() {
let ok_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Ok".to_string()));
map.insert("data".to_string(), Value::Array(Arc::from(vec![]))); map
}));
let ok_pattern = Pattern::Ok(Box::new(Pattern::Identifier("val".to_string())));
assert!(match_pattern(&ok_pattern, &ok_value).is_none());
}
#[test]
fn test_err_pattern_empty_data() {
let err_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::from_string("Err".to_string()));
map.insert("data".to_string(), Value::Array(Arc::from(vec![]))); map
}));
let err_pattern = Pattern::Err(Box::new(Pattern::Identifier("e".to_string())));
assert!(match_pattern(&err_pattern, &err_value).is_none());
}
#[test]
fn test_ok_pattern_non_object() {
let ok_pattern = Pattern::Ok(Box::new(Pattern::Identifier("val".to_string())));
assert!(match_pattern(&ok_pattern, &Value::Integer(42)).is_none());
}
#[test]
fn test_err_pattern_non_object() {
let err_pattern = Pattern::Err(Box::new(Pattern::Identifier("e".to_string())));
assert!(match_pattern(&err_pattern, &Value::Integer(42)).is_none());
}
#[test]
fn test_some_pattern_empty_data() {
let some_value = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![]), };
let some_pattern = Pattern::Some(Box::new(Pattern::Identifier("x".to_string())));
assert!(match_pattern(&some_pattern, &some_value).is_none());
}
#[test]
fn test_some_pattern_none_data() {
let some_value = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: None,
};
let some_pattern = Pattern::Some(Box::new(Pattern::Identifier("x".to_string())));
assert!(match_pattern(&some_pattern, &some_value).is_none());
}
#[test]
fn test_tuple_pattern_on_list() {
let tuple_pattern = Pattern::Tuple(vec![Pattern::Identifier("x".to_string())]);
let list_value = Value::Array(Arc::from(vec![Value::Integer(42)]));
assert!(match_pattern(&tuple_pattern, &list_value).is_none());
}
#[test]
fn test_list_pattern_on_tuple() {
let list_pattern = Pattern::List(vec![Pattern::Identifier("x".to_string())]);
let tuple_value = Value::Tuple(Arc::from(vec![Value::Integer(42)]));
assert!(match_pattern(&list_pattern, &tuple_value).is_none());
}
#[test]
fn test_range_pattern_non_literal_bounds() {
let range_pattern = Pattern::Range {
start: Box::new(Pattern::Identifier("x".to_string())), end: Box::new(Pattern::Literal(Literal::Integer(10, None))),
inclusive: true,
};
assert!(match_pattern(&range_pattern, &Value::Integer(5)).is_none());
}
#[test]
fn test_object_equality_different_values() {
let o1 = Value::Object(Arc::new({
let mut m = HashMap::new();
m.insert("a".to_string(), Value::Integer(1));
m
}));
let o2 = Value::Object(Arc::new({
let mut m = HashMap::new();
m.insert("a".to_string(), Value::Integer(2));
m
}));
assert!(!values_equal(&o1, &o2));
}
#[test]
fn test_values_equal_integer_float_cross() {
assert!(!values_equal(&Value::Integer(42), &Value::Float(42.0)));
}
#[test]
fn test_values_equal_empty_arrays() {
let arr1 = Value::Array(Arc::from(Vec::<Value>::new()));
let arr2 = Value::Array(Arc::from(Vec::<Value>::new()));
assert!(values_equal(&arr1, &arr2));
}
#[test]
fn test_values_equal_nested_arrays() {
let inner = Value::Array(Arc::from(vec![Value::Integer(1)]));
let arr1 = Value::Array(Arc::from(vec![inner.clone()]));
let arr2 = Value::Array(Arc::from(vec![inner]));
assert!(values_equal(&arr1, &arr2));
}
#[test]
fn test_values_equal_tuples_different_length() {
let t1 = Value::Tuple(Arc::from(vec![Value::Integer(1)]));
let t2 = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
assert!(!values_equal(&t1, &t2));
}
#[test]
fn test_values_equal_empty_tuples() {
let t1 = Value::Tuple(Arc::from(Vec::<Value>::new()));
let t2 = Value::Tuple(Arc::from(Vec::<Value>::new()));
assert!(values_equal(&t1, &t2));
}
#[test]
fn test_match_literal_float_near_boundary() {
let tiny_diff = Value::Float(1.0 + f64::EPSILON * 0.4);
assert!(match_literal_pattern(&tiny_diff, &Literal::Float(1.0)));
let larger_diff = Value::Float(1.0 + 0.001);
assert!(!match_literal_pattern(&larger_diff, &Literal::Float(1.0)));
}
#[test]
fn test_values_equal_enum_different_variant_names() {
let v1 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "Some".to_string(),
data: Some(vec![Value::Integer(1)]),
};
let v2 = Value::EnumVariant {
enum_name: "Option".to_string(),
variant_name: "None".to_string(),
data: None,
};
assert!(!values_equal(&v1, &v2));
}
#[test]
fn test_values_equal_range_different_inclusivity() {
let r1 = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: true,
};
let r2 = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: false,
};
assert!(!values_equal(&r1, &r2));
}
#[test]
fn test_values_equal_range_different_start() {
let r1 = Value::Range {
start: Box::new(Value::Integer(1)),
end: Box::new(Value::Integer(10)),
inclusive: true,
};
let r2 = Value::Range {
start: Box::new(Value::Integer(2)),
end: Box::new(Value::Integer(10)),
inclusive: true,
};
assert!(!values_equal(&r1, &r2));
}
#[test]
fn test_match_wildcard_with_nil() {
assert!(match_pattern(&Pattern::Wildcard, &Value::Nil).is_some());
}
#[test]
fn test_match_wildcard_with_range() {
let range = Value::Range {
start: Box::new(Value::Integer(0)),
end: Box::new(Value::Integer(10)),
inclusive: false,
};
assert!(match_pattern(&Pattern::Wildcard, &range).is_some());
}
#[test]
fn test_or_pattern_first_match() {
let or_pat = Pattern::Or(vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Literal(Literal::Integer(2, None)),
]);
let bindings = match_pattern(&or_pat, &Value::Integer(1));
assert!(bindings.is_some());
assert!(bindings.unwrap().is_empty());
}
#[test]
fn test_or_pattern_second_match() {
let or_pat = Pattern::Or(vec![
Pattern::Literal(Literal::Integer(1, None)),
Pattern::Literal(Literal::Integer(2, None)),
]);
let bindings = match_pattern(&or_pat, &Value::Integer(2));
assert!(bindings.is_some());
}
#[test]
fn test_struct_pattern_with_has_rest() {
let struct_value = Value::Object(Arc::new({
let mut map = HashMap::new();
map.insert("a".to_string(), Value::Integer(1));
map.insert("b".to_string(), Value::Integer(2));
map.insert("c".to_string(), Value::Integer(3));
map
}));
let struct_pattern = Pattern::Struct {
name: "Test".to_string(),
fields: vec![StructPatternField {
name: "a".to_string(),
pattern: Some(Pattern::Identifier("x".to_string())),
}],
has_rest: true, };
let binding = match_pattern(&struct_pattern, &struct_value);
assert!(binding.is_some());
}
#[test]
fn test_tuple_variant_wrong_length() {
let variant_pattern = Pattern::TupleVariant {
path: vec!["Some".to_string()],
patterns: vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
],
};
let tuple_value = Value::Tuple(Arc::from(vec![Value::Integer(42)])); assert!(match_pattern(&variant_pattern, &tuple_value).is_none());
}
#[test]
fn test_tuple_variant_match() {
let variant_pattern = Pattern::TupleVariant {
path: vec!["Pair".to_string()],
patterns: vec![
Pattern::Identifier("a".to_string()),
Pattern::Identifier("b".to_string()),
],
};
let tuple_value = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let binding = match_pattern(&variant_pattern, &tuple_value);
assert!(binding.is_some());
let bindings = binding.unwrap();
assert_eq!(bindings.len(), 2);
}
#[test]
fn test_values_equal_string_empty() {
let s1 = Value::from_string("".to_string());
let s2 = Value::from_string("".to_string());
assert!(values_equal(&s1, &s2));
}
#[test]
fn test_values_equal_string_whitespace() {
let s1 = Value::from_string(" ".to_string());
let s2 = Value::from_string("".to_string());
assert!(!values_equal(&s1, &s2));
}
#[test]
fn test_values_equal_negative_integer() {
assert!(values_equal(&Value::Integer(-42), &Value::Integer(-42)));
assert!(!values_equal(&Value::Integer(-42), &Value::Integer(-43)));
}
#[test]
fn test_match_identifier_with_tuple() {
let tuple = Value::Tuple(Arc::from(vec![Value::Integer(1), Value::Integer(2)]));
let binding = match_pattern(&Pattern::Identifier("x".to_string()), &tuple);
assert!(binding.is_some());
let bindings = binding.unwrap();
assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].0, "x");
}
}