use crate::symbol::{ContextRef, RelativeContext, SymbolNameRef, SymbolRef};
use crate::{
expr, Association, ByteArray, Expr, ExprKind, NumericArray, NumericArrayEnum,
PackedArray, PackedArrayEnum, Symbol,
};
#[rustfmt::skip]
const DATA: &[(&str, bool, bool, bool, bool)] = &[
("foo`bar", true , false, false, false),
("foo`bar`baz", true , false, false, false),
("foo`bar5", true , false, false, false),
("foo`5bar", false, false, false, false),
("5foo`bar", false, false, false, false),
("foo``bar", false, false, false, false),
("foo`$bar", true , false, false, false),
("$foo`$bar", true , false, false, false),
("$foo`$$$", true , false, false, false),
("$$$`$$$", true , false, false, false),
("foo", false, true, false, false),
("foo5", false, true, false, false),
("foo5bar", false, true, false, false),
("$foo", false, true, false, false),
("5foo", false, false, false, false),
("foo_bar", false, false, false, false),
("_foo", false, false, false, false),
("`foo", false, false, false, false),
("`foo`bar", false, false, false, false),
("foo`", false, false, true, false),
("foo`bar`", false, false, true, false),
("`foo`", false, false, false, true),
("`foo`bar`", false, false, false, true),
];
#[test]
pub fn test_symbol_like_parsing() {
for (input, is_symbol, is_symbol_name, is_context, is_rel_context) in
DATA.iter().copied()
{
println!("input: {input}");
assert_eq!(SymbolRef::try_new(input).is_some(), is_symbol);
assert_eq!(SymbolNameRef::try_new(input).is_some(), is_symbol_name);
assert_eq!(ContextRef::try_new(input).is_some(), is_context);
assert_eq!(RelativeContext::try_new(input).is_some(), is_rel_context);
}
}
#[test]
fn byte_array_variant_roundtrip() {
let ba = ByteArray::from(vec![0x01, 0x02, 0x03, 0xff]);
let expr = Expr::from(ba.clone());
match expr.kind() {
ExprKind::ByteArray(got) => assert_eq!(got, &ba),
other => panic!("expected ByteArray, got {:?}", other),
}
assert!(expr.tag().is_none());
}
#[test]
fn association_variant_roundtrip() {
use crate::RuleEntry;
let a: Association = vec![
RuleEntry::rule(Expr::from("k1"), Expr::from(1)),
RuleEntry::rule_delayed(Expr::from("k2"), Expr::from(2)),
];
let expr = Expr::from(a.clone());
let ExprKind::Association(extracted) = expr.kind() else {
panic!("expected Association, got {:?}", expr.kind());
};
assert_eq!(extracted, &a);
let mut it = extracted.iter();
let e0 = it.next().unwrap();
assert_eq!(e0.key, Expr::from("k1"));
assert_eq!(e0.value, Expr::from(1));
assert!(!e0.delayed);
let e1 = it.next().unwrap();
assert_eq!(e1.key, Expr::from("k2"));
assert_eq!(e1.value, Expr::from(2));
assert!(e1.delayed);
assert!(it.next().is_none());
}
#[test]
fn numeric_array_variant_roundtrip() {
let arr = NumericArray::from_slice::<i32>(vec![2, 2], &[10, 20, 30, 40]);
let expr = Expr::from(arr.clone());
let ExprKind::NumericArray(got) = expr.kind() else {
panic!("expected NumericArray, got {:?}", expr.kind());
};
assert_eq!(got.dimensions(), &[2, 2]);
assert_eq!(got.data_type(), NumericArrayEnum::Integer32);
assert_eq!(got.try_as_slice::<i32>(), Some([10, 20, 30, 40].as_slice()));
}
#[test]
fn packed_array_variant_roundtrip() {
let arr = PackedArray::from_slice::<f64>(vec![3], &[1.0, 2.0, 3.0]);
let expr = Expr::from(arr.clone());
let ExprKind::PackedArray(got) = expr.kind() else {
panic!("expected PackedArray, got {:?}", expr.kind());
};
assert_eq!(got.dimensions(), &[3]);
assert_eq!(got.data_type(), PackedArrayEnum::Real64);
assert_eq!(got.try_as_slice::<f64>(), Some([1.0, 2.0, 3.0].as_slice()));
}
#[test]
fn new_variants_have_no_tag() {
let sym = expr!(Global::x);
assert!(sym.tag().is_some());
let ba = Expr::from(ByteArray::from(vec![1, 2, 3]));
let na = Expr::from(NumericArray::from_slice::<i64>(vec![3], &[1, 2, 3]));
let pa = Expr::from(PackedArray::from_slice::<i64>(vec![3], &[1, 2, 3]));
let assoc = Expr::from(Association::new());
assert!(ba.tag().is_none());
assert!(na.tag().is_none());
assert!(pa.tag().is_none());
assert!(assoc.tag().is_none());
}
#[test]
fn new_variants_have_no_normal_head() {
let ba = Expr::from(ByteArray::new());
let na = Expr::from(NumericArray::from_slice::<u8>(vec![0], &[]));
assert!(ba.normal_head().is_none());
assert!(na.normal_head().is_none());
}
#[test]
fn display_of_new_variants_is_non_empty() {
let ba = Expr::from(ByteArray::from(vec![0xab]));
let assoc = {
use crate::RuleEntry;
let a: Association = vec![RuleEntry::rule(Expr::from("k"), Expr::from(1))];
Expr::from(a)
};
let na = Expr::from(NumericArray::from_slice::<u8>(vec![1], &[42]));
let pa = Expr::from(PackedArray::from_slice::<i32>(vec![1], &[42]));
assert!(format!("{}", ba).starts_with("ByteArray[\"") && format!("{}", ba).ends_with("\"]"));
assert!(
format!("{}", assoc).starts_with("<|") && format!("{}", assoc).ends_with("|>")
);
assert!(format!("{}", na).starts_with("BinaryDeserialize[ByteArray[\""));
assert!(format!("{}", pa).starts_with("BinaryDeserialize[ByteArray[\""));
}
#[test]
fn display_uses_wl_surface_syntax() {
assert_eq!(expr!(System::List[1, 2, 3]).to_string(), "{1, 2, 3}");
assert_eq!(expr!(::List[1, 2]).to_string(), "{1, 2}");
assert_eq!(expr!(System::Rule[1, 2]).to_string(), "1 -> 2");
assert_eq!(expr!(System::RuleDelayed[1, 2]).to_string(), "1 :> 2");
assert_eq!(expr!(System::Set[1, 2]).to_string(), "1 = 2");
assert_eq!(expr!(System::Slot[1]).to_string(), "#1");
assert_eq!(expr!(::Slot["foo"]).to_string(), "#foo");
assert_eq!(expr!(System::SlotSequence[1]).to_string(), "##1");
assert_eq!(expr!(::SlotSequence["bar"]).to_string(), "##bar");
assert_eq!(expr!(System::Foo[1, 2]).to_string(), "System`Foo[1, 2]");
assert_eq!(expr!(System::Set[1, 2, 3]).to_string(), "System`Set[1, 2, 3]");
assert_eq!(expr!(System::Slot[System::x]).to_string(), "System`Slot[System`x]");
assert_eq!(expr!(System::Slot[1, 2]).to_string(), "System`Slot[1, 2]");
}
#[test]
fn big_integer_variant_roundtrip() {
use crate::BigInteger;
let huge = BigInteger("999999999999999999999999999999".into());
let expr = Expr::from(huge.clone());
match expr.kind() {
ExprKind::BigInteger(n) => assert_eq!(n, &huge),
other => panic!("expected BigInteger, got {:?}", other),
}
}
#[test]
fn expr_macro_nested_head_in_arg() {
let by_macro = expr!(System::Style[System::Foo[2]]);
let by_hand = Expr::normal(
Symbol::new("System`Style"),
vec![Expr::normal(Symbol::new("System`Foo"), vec![Expr::from(2)])],
);
assert_eq!(by_macro, by_hand);
let by_macro = expr!(System::Style[System::Foo[System::Bar[3]], "x" -> "y"]);
let by_hand = Expr::normal(
Symbol::new("System`Style"),
vec![
Expr::normal(
Symbol::new("System`Foo"),
vec![Expr::normal(Symbol::new("System`Bar"), vec![Expr::from(3)])],
),
Expr::normal(
Symbol::new("System`Rule"),
vec![Expr::from("x"), Expr::from("y")],
),
],
);
assert_eq!(by_macro, by_hand);
}
#[test]
fn expr_macro_inline_rule() {
let col = Expr::from("content");
let by_macro = expr!(System::Style[col, "FontFamily" -> "Courier"]);
let col = Expr::from("content");
let by_hand = Expr::normal(
Symbol::new("System`Style"),
vec![
col,
Expr::normal(
Symbol::new("System`Rule"),
vec![Expr::from("FontFamily"), Expr::from("Courier")],
),
],
);
assert_eq!(by_macro, by_hand);
}
#[test]
fn expr_macro_contextless_symbols_nest() {
let items = vec![Expr::from(1), Expr::from(2)];
let by_macro = expr!(::List[::Inner["x", ::Bare], ::List[..items], ::$Context]);
let no_ctx = |name: &str, args: Vec<Expr>| {
Expr::normal(Symbol::new(name), args)
};
let by_hand = no_ctx(
"List",
vec![
no_ctx("Inner", vec![Expr::from("x"), Expr::symbol(Symbol::new("Bare"))]),
no_ctx("List", vec![Expr::from(1), Expr::from(2)]),
Expr::symbol(Symbol::new("$Context")),
],
);
assert_eq!(by_macro, by_hand);
assert_eq!(format!("{}", expr!(::$InputFileName)), "$InputFileName");
let assoc = expr!({"a" -> ::Inner["v"], "b" -> ::Bare});
let ExprKind::Association(entries) = assoc.kind() else {
panic!("expected Association, got {}", assoc);
};
let mut it = entries.iter();
assert_eq!(it.next().unwrap().value, no_ctx("Inner", vec![Expr::from("v")]));
assert_eq!(it.next().unwrap().value, Expr::symbol(Symbol::new("Bare")));
}
#[test]
fn big_real_variant_roundtrip() {
use crate::BigReal;
let r = BigReal("3.14159265358979323846`50.".into());
let expr = Expr::from(r.clone());
match expr.kind() {
ExprKind::BigReal(s) => assert_eq!(s, &r),
other => panic!("expected BigReal, got {:?}", other),
}
}