runmat-vm 0.6.0

RunMat virtual machine and bytecode interpreter
Documentation
use runmat_builtins::{StringArray, Tensor, Value};

#[path = "support/mod.rs"]
mod test_helpers;
use test_helpers::execute_source;

#[test]
fn datetime_construction_and_component_access_work_in_scripts() {
    let vars =
        execute_source("d = datetime(2024, 3, 14); y = year(d); m = month(d); daynum = day(d);")
            .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Object(obj) if obj.class_name == "datetime")));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 2024.0).abs() < f64::EPSILON)));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 3.0).abs() < f64::EPSILON)));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 14.0).abs() < f64::EPSILON)));
}

#[test]
fn datetime_string_and_indexing_work_for_arrays() {
    let vars = execute_source(
        "d = datetime([2024 2025], [1 6], [15 20]); d2 = d(2); y = year(d2); s = string(d);",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 2025.0).abs() < f64::EPSILON)));
    assert!(vars.iter().any(|value| match value {
        Value::StringArray(StringArray { data, .. }) => {
            data.iter().any(|text| text == "15-Jan-2024")
                && data.iter().any(|text| text == "20-Jun-2025")
        }
        _ => false,
    }));
}

#[test]
fn datetime_comparisons_and_format_assignment_work() {
    let vars = execute_source(
        "a = datetime(2024, 1, 1); \
         b = datetime(2024, 1, 2); \
         ok = a < b; \
         a.Format = 'yyyy-MM-dd'; \
         c = char(a);",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 1.0).abs() < f64::EPSILON)));
    assert!(vars.iter().any(|value| match value {
        Value::CharArray(array) => {
            let rendered: String = array.data.iter().collect();
            rendered.trim_end() == "2024-01-01"
        }
        _ => false,
    }));
}

#[test]
fn datetime_addition_and_subtraction_return_day_deltas() {
    let vars = execute_source(
        "t0 = datetime(2024, 4, 9); \
         t1 = t0 + 7; \
         delta = t1 - t0;",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 7.0).abs() < f64::EPSILON)));
}

#[test]
fn datetime_subtraction_between_scalars_uses_object_overload_path() {
    let vars = execute_source(
        "t0 = datetime(2024, 4, 9); \
         t1 = datetime(2024, 4, 16); \
         delta = t1 - t0;",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 7.0).abs() < f64::EPSILON)));
}

#[test]
fn datetime_legacy_conversion_helpers_execute_in_scripts() {
    let vars = execute_source(
        "n = datenum(2024, 3, 14); \
         v = datevec(n); \
         w = weekday(n); \
         e = eomday(2024, 2); \
         s = datestr(n, 'yyyy-MM-dd'); \
         ok = isbetween(n, n - 1, n + 1);",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 5.0).abs() < f64::EPSILON)));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 29.0).abs() < f64::EPSILON)));
    assert!(vars.iter().any(|value| match value {
        Value::CharArray(array) => {
            let rendered: String = array.data.iter().collect();
            rendered.trim_end() == "2024-03-14"
        }
        _ => false,
    }));
}

#[test]
fn calendar_duration_helpers_execute_in_scripts() {
    let vars = execute_source(
        "t = datetime(2024, 1, 31); \
         c = calmonths(1); \
         shifted = t + c; \
         shifted.Format = 'yyyy-MM-dd'; \
         txt = char(shifted); \
         m = calmonths(c); \
         p = iscalendarduration(c);",
    )
    .unwrap();

    assert!(vars.iter().any(|value| match value {
        Value::CharArray(array) => {
            let rendered: String = array.data.iter().collect();
            rendered.trim_end() == "2024-02-29"
        }
        _ => false,
    }));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 1.0).abs() < f64::EPSILON)));
    assert!(vars.iter().any(|value| matches!(value, Value::Bool(true))));
}

#[test]
fn business_day_helpers_execute_in_scripts() {
    let vars = execute_source(
        "f = fbusdate(2024, 6); \
         l = lbusdate(2024, 6); \
         span = daysdif(f, l); \
         count = days252bus(f, f + 3); \
         mask = isbusday([f f + 1]);",
    )
    .unwrap();

    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 739_406.0).abs() < f64::EPSILON)));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 25.0).abs() < f64::EPSILON)));
    assert!(vars
        .iter()
        .any(|value| matches!(value, Value::Num(n) if (*n - 4.0).abs() < f64::EPSILON)));
    assert!(vars.iter().any(
        |value| matches!(value, Value::Tensor(Tensor { data, .. }) if data == &vec![1.0, 1.0])
    ));
}