use rprt_engine::{ evaluate::evaluate, expression::{BuiltinSelectionFn, ResultTransform, SearchModifier}, parser::parse, selection::{Interval, Selection}, selection_functions::{SelectionFunctionError, evaluate_selection_function}, state::EditorState, token::tokenise, }; fn state_with_content(content: &str) -> EditorState { let mut es = EditorState::new(); es.create_buffer("test".to_string(), content.to_string(), None); es } /// Parse `input` and evaluate it against a fresh state holding `content`. fn eval_str(content: &str, input: &str) -> Result { let mut es = state_with_content(content); let tokens = tokenise(input).map_err(|e| format!("tokenise: {e:?}"))?; let comp = parse(tokens).map_err(|e| format!("parse: {e:?}"))?; evaluate(&mut es, comp).map_err(|e| format!("evaluate: {e:?}")) } fn assert_scalar(res: Selection, pos: usize) { match res { Selection::Scalar { buffer_id, pos: p } => { assert_eq!(buffer_id, 0); assert_eq!(p, pos, "expected scalar at {pos}"); } other => panic!("expected scalar at {pos}, got {other:?}"), } } #[test] fn niladic_character() { assert_scalar(eval_str("hello world", "#5").unwrap(), 5); // clamped to buffer end assert_scalar(eval_str("hello world", "#100").unwrap(), 11); } #[test] fn niladic_end_unparameterised() { // These go through UnparameterisedSelectionFunction + the // SelectionFunction<()> blanket impl. assert_scalar(eval_str("hello world", "$").unwrap(), 11); assert_scalar(eval_str("hello world", "'$").unwrap(), 0); assert!(eval_str("hello world", ";$").is_err()); assert!(eval_str("hello world", "';$").is_err()); assert!(eval_str("hello world", ";'$").is_err()); } #[test] fn monadic_over_scalar() { // Train: f = #5 (niladic), g = $ applied to f's result (rank-0 vectorise). assert_scalar(eval_str("hello world", "#5 $").unwrap(), 5); // "α ;$" = "End of buffer" assert_scalar(eval_str("hello world", "#5 ;$").unwrap(), 11); assert_scalar(eval_str("hello world", "#5 '$").unwrap(), 5); } #[test] fn character_within_interval() { // "α #n" rank 1: "Char n within α", offset from the interval's start, // clamped at its end. let es = state_with_content("hello world"); let interval = || Selection::Vector { buffer_id: 0, interval: Interval::new(2, 9), }; let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(3), None, None, Some(interval()), None, ) .unwrap(); assert_scalar(res, 5); // 2 + 3 // clamped at the interval's end let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(100), None, None, Some(interval()), None, ) .unwrap(); assert_scalar(res, 9); } #[test] fn search_modifiers_character() { // ;#1: min(pos + 1, max) assert_scalar(eval_str("hello world", "#2 ;#1").unwrap(), 3); // ';#1: pos - 1 assert_scalar(eval_str("hello world", "#2 ';#1").unwrap(), 1); } #[test] fn reverse_character() { // '#3 niladic: "Character 3 from end of buffer" assert_scalar(eval_str("hello world", "'#3").unwrap(), 8); // "α '#n" rank 1: "Char n from end of α" --- offset back from the // interval's end boundary, clamped at 0. let es = state_with_content("hello world"); let interval = || Selection::Vector { buffer_id: 0, interval: Interval::new(2, 9), }; let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(3), Some(SearchModifier::Reverse), None, Some(interval()), None, ) .unwrap(); assert_scalar(res, 6); // 9 - 3 // n larger than the interval's length clamps to 0. let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(10), Some(SearchModifier::Reverse), None, Some(interval()), None, ) .unwrap(); assert_scalar(res, 0); } #[test] fn monadic_over_vectors() { // #100 applied to a rank-2 selection: min(interval.end, 100) per range. let es = state_with_content("hello world"); let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(100), None, None, Some(Selection::Vectors { buffer_id: 0, ranges: vec![Interval::new(2, 4), Interval::new(6, 9)], }), None, ) .unwrap(); match res { Selection::Vectors { buffer_id, ranges } => { assert_eq!(buffer_id, 0); assert_eq!(ranges.len(), 2); assert_eq!((ranges[0].start, ranges[0].end), (4, 5)); assert_eq!((ranges[1].start, ranges[1].end), (9, 10)); } other => panic!("expected Vectors, got {other:?}"), } } #[test] fn complement_transform() { // ~#5 on "hello world": complement of pos 5 is [0..5) and (6..11]. let es = state_with_content("hello world"); let res = evaluate_selection_function( &es, BuiltinSelectionFn::CharOffset(5), None, Some(ResultTransform::Complement), None, None, ) .unwrap(); match res { Selection::Vectors { buffer_id, ranges } => { assert_eq!(buffer_id, 0); assert_eq!(ranges.len(), 2); assert_eq!((ranges[0].start, ranges[0].end), (0, 5)); assert_eq!((ranges[1].start, ranges[1].end), (6, 11)); } other => panic!("expected Vectors, got {other:?}"), } } #[test] fn empty_selection() { // `e` and `α e`: empty selection of rank 3. match eval_str("hello world", "e").unwrap() { Selection::MultiVectors { multi_ranges } => assert!(multi_ranges.is_empty()), other => panic!("expected rank-3 empty, got {other:?}"), } match eval_str("hello world", "#5 e").unwrap() { Selection::MultiVectors { multi_ranges } => assert!(multi_ranges.is_empty()), other => panic!("expected rank-3 empty, got {other:?}"), } // `'e` and `;e`: empty selection of rank 2, anchored to the buffer. match eval_str("hello world", "#5 'e").unwrap() { Selection::Vectors { buffer_id, ranges } => { assert_eq!(buffer_id, 0); assert!(ranges.is_empty()); } other => panic!("expected rank-2 empty, got {other:?}"), } match eval_str("hello world", "#5 ;e").unwrap() { Selection::Vectors { buffer_id, ranges } => { assert_eq!(buffer_id, 0); assert!(ranges.is_empty()); } other => panic!("expected rank-2 empty, got {other:?}"), } // `;e` and friends have no niladic form. assert!(eval_str("hello world", ";e").is_err()); assert!(eval_str("hello world", ";'e").is_err()); assert!(eval_str("hello world", "';e").is_err()); } #[test] fn dyadic_is_rejected() { let es = state_with_content("hello world"); let scalar = || Selection::Scalar { buffer_id: 0, pos: 0, }; let res = evaluate_selection_function( &es, BuiltinSelectionFn::EndOfBuffer, None, None, Some(scalar()), Some(scalar()), ); match res { Err(SelectionFunctionError::NoDyadicForm(name)) => { assert_eq!(name, "end"); } other => panic!("expected NoDyadicForm, got {other:?}"), } }