sim-lib-pattern 0.2.0

Shape-based pattern matching and destructuring for SIM runtime values.
Documentation
use sim_lib_pattern::{
    AssertionId, ByteDomain, ByteOffset, CaptureId, CodeUnitDomain, CodeUnitOffset, Cursor,
    EnginePolicy, IrError, IrNode, PatternIr, RepeatBounds,
};
use std::collections::BTreeMap;

#[test]
fn cursor_preserves_independent_positions() {
    let byte_cursor = Cursor::<ByteDomain>::new(ByteOffset(3), ByteOffset(11));
    assert_eq!(byte_cursor.source_position(), ByteOffset(3));
    assert_eq!(byte_cursor.subject_position(), ByteOffset(11));

    let code_unit_cursor =
        Cursor::<CodeUnitDomain>::new(CodeUnitOffset::new(2), CodeUnitOffset::new(7));
    assert_eq!(code_unit_cursor.source_position(), CodeUnitOffset::new(2));
    assert_eq!(code_unit_cursor.subject_position(), CodeUnitOffset::new(7));
}

#[test]
fn invalid_repeat_names_both_bounds() {
    let error = RepeatBounds::new(4, Some(3)).unwrap_err();
    assert_eq!(
        error.to_string(),
        "invalid repeat bounds: minimum 4 exceeds maximum 3"
    );
}

#[test]
fn validation_rejects_duplicate_captures_cycles_and_extensions() {
    let capture = |symbol| IrNode::Capture {
        id: CaptureId(7),
        node: Box::new(IrNode::Symbol(symbol)),
    };
    let duplicate = PatternIr::<ByteDomain, &str>::new(
        IrNode::Concat(vec![capture(b'a'), capture(b'b')]),
        BTreeMap::new(),
        &EnginePolicy::new([]),
    );
    assert_eq!(
        duplicate.unwrap_err(),
        IrError::DuplicateCapture(CaptureId(7))
    );

    let assertions = BTreeMap::from([
        (AssertionId(1), IrNode::Assertion(AssertionId(2))),
        (AssertionId(2), IrNode::Assertion(AssertionId(1))),
    ]);
    let cycle = PatternIr::<ByteDomain, &str>::new(
        IrNode::Assertion(AssertionId(1)),
        assertions,
        &EnginePolicy::new([]),
    );
    assert!(matches!(cycle, Err(IrError::AssertionCycle(_))));

    let denied = PatternIr::<ByteDomain, &str>::new(
        IrNode::Extension("backreference"),
        BTreeMap::new(),
        &EnginePolicy::new([]),
    );
    assert!(matches!(denied, Err(IrError::UnsupportedExtension(_))));
}