runmat-ignition 0.4.1

Baseline interpreter for RunMat enabling instant startup and execution
Documentation
use runmat_builtins::{StringArray, Value};
use runmat_parser::parse;

mod test_helpers;
use test_helpers::{execute, lower};

#[test]
fn datetime_construction_and_component_access_work_in_scripts() {
    let ast =
        parse("d = datetime(2024, 3, 14); y = year(d); m = month(d); daynum = day(d);").unwrap();
    let hir = lower(&ast).unwrap();
    let vars = execute(&hir).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 ast =
        parse("d = datetime([2024 2025], [1 6], [15 20]); d2 = d(2); y = year(d2); s = string(d);")
            .unwrap();
    let hir = lower(&ast).unwrap();
    let vars = execute(&hir).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 ast = parse(
        "a = datetime(2024, 1, 1); \
         b = datetime(2024, 1, 2); \
         ok = a < b; \
         a.Format = 'yyyy-MM-dd'; \
         c = char(a);",
    )
    .unwrap();
    let hir = lower(&ast).unwrap();
    let vars = execute(&hir).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 ast = parse(
        "t0 = datetime(2024, 4, 9); \
         t1 = t0 + 7; \
         delta = t1 - t0;",
    )
    .unwrap();
    let hir = lower(&ast).unwrap();
    let vars = execute(&hir).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 ast = parse(
        "t0 = datetime(2024, 4, 9); \
         t1 = datetime(2024, 4, 16); \
         delta = t1 - t0;",
    )
    .unwrap();
    let hir = lower(&ast).unwrap();
    let vars = execute(&hir).unwrap();

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