Fix type-of and comparison for new Value variants
This commit is contained in:
+44
-18
@@ -381,6 +381,11 @@ fn register_comparison(env: &Environment) {
|
|||||||
(Value::Record(la, va), Value::Record(lb, vb)) => {
|
(Value::Record(la, va), Value::Record(lb, vb)) => {
|
||||||
Value::Bool(std::sync::Arc::ptr_eq(la, lb) && va == vb)
|
Value::Bool(std::sync::Arc::ptr_eq(la, lb) && va == vb)
|
||||||
}
|
}
|
||||||
|
(Value::Tuple(a), Value::Tuple(b)) => {
|
||||||
|
Value::Bool(a.len() == b.len()
|
||||||
|
&& a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y)))
|
||||||
|
}
|
||||||
|
(Value::FieldAccessor(a), Value::FieldAccessor(b)) => Value::Bool(a == b),
|
||||||
(Value::Void, Value::Void) => Value::Bool(true),
|
(Value::Void, Value::Void) => Value::Bool(true),
|
||||||
_ => Value::Bool(false),
|
_ => Value::Bool(false),
|
||||||
}
|
}
|
||||||
@@ -401,6 +406,11 @@ fn register_comparison(env: &Environment) {
|
|||||||
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a != b),
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a != b),
|
||||||
(Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a != b),
|
(Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a != b),
|
||||||
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a != b),
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a != b),
|
||||||
|
(Value::Tuple(a), Value::Tuple(b)) => {
|
||||||
|
Value::Bool(!(a.len() == b.len()
|
||||||
|
&& a.iter().zip(b.iter()).all(|(x, y)| values_equal(x, y))))
|
||||||
|
}
|
||||||
|
(Value::FieldAccessor(a), Value::FieldAccessor(b)) => Value::Bool(a != b),
|
||||||
(Value::Void, Value::Void) => Value::Bool(false),
|
(Value::Void, Value::Void) => Value::Bool(false),
|
||||||
_ => Value::Bool(true),
|
_ => Value::Bool(true),
|
||||||
}
|
}
|
||||||
@@ -605,24 +615,40 @@ fn register_testing(env: &Environment) {
|
|||||||
}));
|
}));
|
||||||
env.register_native_fn("type-of", type_of_ty, Purity::Pure, |args| {
|
env.register_native_fn("type-of", type_of_ty, Purity::Pure, |args| {
|
||||||
let name = match args.first() {
|
let name = match args.first() {
|
||||||
Some(v) => match v.static_type() {
|
// Match on Value directly to avoid lossy static_type() conversion
|
||||||
StaticType::Void => "void",
|
// (e.g. Value::Function → StaticType::Any loses the "function" info).
|
||||||
StaticType::Bool => "bool",
|
Some(v) => match v {
|
||||||
StaticType::Int => "int",
|
Value::Void => "void",
|
||||||
StaticType::Float => "float",
|
Value::Bool(_) => "bool",
|
||||||
StaticType::DateTime => "datetime",
|
Value::Int(_) => "int",
|
||||||
StaticType::Text => "text",
|
Value::Float(_) => "float",
|
||||||
StaticType::Keyword => "keyword",
|
Value::DateTime(_) => "datetime",
|
||||||
StaticType::Tuple(_)
|
Value::Text(_) => "text",
|
||||||
| StaticType::Vector(_, _)
|
Value::Keyword(_) => "keyword",
|
||||||
| StaticType::Matrix(_, _) => "tuple",
|
Value::Tuple(_) => "tuple",
|
||||||
StaticType::Record(_) => "record",
|
Value::Record(_, _) => "record",
|
||||||
StaticType::Series(_) => "series",
|
Value::FieldAccessor(_) => "field-accessor",
|
||||||
StaticType::Stream(_) => "stream",
|
Value::Function(_) | Value::Closure(_) => "function",
|
||||||
StaticType::Function(_) | StaticType::FunctionOverloads(_) => "function",
|
Value::Quote(_) => "quote",
|
||||||
StaticType::FieldAccessor(_) => "field-accessor",
|
Value::Series(_) => "series",
|
||||||
StaticType::Object(name) => name,
|
Value::Stream(_) => "stream",
|
||||||
_ => "unknown",
|
Value::Cell(c) => {
|
||||||
|
// Recurse through Cell to get the inner type.
|
||||||
|
// We can't recurse here directly, so use static_type fallback.
|
||||||
|
match c.borrow().static_type() {
|
||||||
|
StaticType::Void => "void",
|
||||||
|
StaticType::Bool => "bool",
|
||||||
|
StaticType::Int => "int",
|
||||||
|
StaticType::Float => "float",
|
||||||
|
StaticType::DateTime => "datetime",
|
||||||
|
StaticType::Text => "text",
|
||||||
|
StaticType::Keyword => "keyword",
|
||||||
|
StaticType::Record(_) => "record",
|
||||||
|
StaticType::Series(_) => "series",
|
||||||
|
StaticType::Stream(_) => "stream",
|
||||||
|
_ => "unknown",
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => panic!("type-of requires 1 argument"),
|
None => panic!("type-of requires 1 argument"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -138,6 +138,103 @@ fn test_environments_from_shared_rtl_are_independent() {
|
|||||||
assert_eq!(format!("{}", env2.run_script("(+ 1 2)").unwrap()), "3");
|
assert_eq!(format!("{}", env2.run_script("(+ 1 2)").unwrap()), "3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Bug fixes for wildcard matches ---
|
||||||
|
|
||||||
|
/// `type-of` must return :function for native functions (e.g. `print`).
|
||||||
|
/// Previously returned :unknown because Value::Function.static_type() yields Any,
|
||||||
|
/// and the type-of match did not handle Any.
|
||||||
|
#[test]
|
||||||
|
fn test_type_of_native_function() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(type-of print)").unwrap()),
|
||||||
|
":function",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `type-of` must return :function for closures, not :closure.
|
||||||
|
/// Closures are functions from the user's perspective.
|
||||||
|
#[test]
|
||||||
|
fn test_type_of_closure() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(type-of (fn [x] x))").unwrap()),
|
||||||
|
":function",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `type-of` must return :field-accessor for field accessors.
|
||||||
|
#[test]
|
||||||
|
fn test_type_of_field_accessor() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(type-of .name)").unwrap()),
|
||||||
|
":field-accessor",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `=` operator must compare tuples structurally, not return false.
|
||||||
|
#[test]
|
||||||
|
fn test_equality_tuples() {
|
||||||
|
let env = Environment::new();
|
||||||
|
// Use def bindings because vector literals in call position get flattened.
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a [1 2 3]) (def b [1 2 3]) (= a b))").unwrap()),
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a [1 2 3]) (def b [1 2 4]) (= a b))").unwrap()),
|
||||||
|
"false",
|
||||||
|
);
|
||||||
|
// Nested tuples
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a [[1 2] [3 4]]) (def b [[1 2] [3 4]]) (= a b))").unwrap()),
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `<>` operator must compare tuples structurally.
|
||||||
|
#[test]
|
||||||
|
fn test_inequality_tuples() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a [1 2 3]) (def b [1 2 3]) (<> a b))").unwrap()),
|
||||||
|
"false",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a [1 2 3]) (def b [1 2 4]) (<> a b))").unwrap()),
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `=` operator must compare field accessors by identity.
|
||||||
|
#[test]
|
||||||
|
fn test_equality_field_accessors() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a .name) (def b .name) (= a b))").unwrap()),
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a .name) (def b .age) (= a b))").unwrap()),
|
||||||
|
"false",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `<>` operator must compare field accessors by identity.
|
||||||
|
#[test]
|
||||||
|
fn test_inequality_field_accessors() {
|
||||||
|
let env = Environment::new();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a .name) (def b .age) (<> a b))").unwrap()),
|
||||||
|
"true",
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", env.run_script("(do (def a .name) (def b .name) (<> a b))").unwrap()),
|
||||||
|
"false",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Arithmetic operators with incompatible types (e.g. Float - Record)
|
/// Arithmetic operators with incompatible types (e.g. Float - Record)
|
||||||
/// must be caught at compile time, not silently return Void at runtime.
|
/// must be caught at compile time, not silently return Void at runtime.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user