use crate::expression::*; use crate::token::*; #[derive(Debug, Clone)] pub enum ParseError { UnexpectedToken { found: Token, // Own the token, don't borrow it expecting: &'static str, }, UnexpectedEndOfInput { expecting: &'static str, }, } struct ParseContext { tokens: Vec, pos: usize, } impl ParseContext { fn advance(&mut self, how_much: usize, need_more: bool) -> Result<(), ParseError> { self.pos += how_much; if need_more && self.pos >= self.tokens.len() { Err(ParseError::UnexpectedEndOfInput { expecting: "more tokens", }) } else { Ok(()) } } fn current(&self) -> Result { self.tokens .get(self.pos) .cloned() .ok_or(ParseError::UnexpectedEndOfInput { expecting: "token" }) } fn peek(&self, offset: usize) -> Option<&Token> { self.tokens.get(self.pos + offset) } } pub fn parse(tokens: Vec) -> Result { let mut context = ParseContext { tokens: tokens, pos: 0, }; parse_function_in_application(&mut context) } fn parse_function_in_application(context: &mut ParseContext) -> Result { let mut functions = Vec::new(); while can_start_selection_function(context, 0) { functions.push(parse_function_atom(context)?); } if functions.is_empty() { return Err(ParseError::UnexpectedToken { found: context.current()?, expecting: "function", }); } Ok(build_train(functions)) } fn build_train(mut functions: Vec) -> Composite { // Rust is insane and has no easy way to destructure a vector and own the elements // so this function is littered with unwraps for no reason, we already own the vector! match functions.len() { 1 => functions.pop().unwrap(), 2 => { let [f, g] = <[_; 2]>::try_from(functions).unwrap(); Composite::Train2 { f: Box::new(f), g: Box::new(g), } } 3 => { let [f, g, h] = <[_; 3]>::try_from(functions).unwrap(); Composite::Train3 { f: Box::new(f), g: Box::new(g), h: Box::new(h), } } n if n % 2 == 0 => { let rest = functions.split_off(1); let [f] = <[_; 1]>::try_from(functions).unwrap(); Composite::Train2 { f: Box::new(f), g: Box::new(build_train(rest)), } } _ => { let rest = functions.split_off(2); let [f, g] = <[_; 2]>::try_from(functions).unwrap(); Composite::Train3 { f: Box::new(f), g: Box::new(g), h: Box::new(build_train(rest)), } } } } fn can_start_selection_function(context: &ParseContext, offset: usize) -> bool { matches!( context.peek(offset), Some(Token::Empty) | Some(Token::EndOfBuffer) | Some(Token::Span) | Some(Token::CharacterOffset) | Some(Token::Number(_)) | Some(Token::Regex(_)) | Some(Token::AllMatches) | Some(Token::LineOffset) | Some(Token::BufferMatch) | Some(Token::Reverse) | Some(Token::Sequential) | Some(Token::Complement) | Some(Token::Conditional) | Some(Token::LParen) ) } fn parse_function_atom(context: &mut ParseContext) -> Result { if matches!(context.current()?, Token::LParen) { context.advance(1, true)?; let func = parse_function_in_application(context)?; if !matches!(context.current()?, Token::RParen) { return Err(ParseError::UnexpectedToken { found: context.current()?, expecting: ")", }); } context.advance(1, false)?; return Ok(func); } let result_trans = parse_result_trans(context)?; let search_mod = parse_search_mod(context)?; let func = parse_builtin_function_s(context)?; Ok(Composite::SelectionFunction { func: func, search_mod: search_mod, result_transform: result_trans, }) } fn parse_search_mod(context: &mut ParseContext) -> Result, ParseError> { let maybe_mod = |token| match token { Token::Sequential => Ok(Some(SearchModifier::Sequential)), Token::Reverse => Ok(Some(SearchModifier::Reverse)), _ => Ok(None), }; let token = context.current()?; if let Some(m1) = maybe_mod(token)? { context.advance(1, true)?; if let Some(m2) = maybe_mod(context.current()?)? { context.advance(1, true)?; return Ok(Some(match (m1, m2) { (SearchModifier::Sequential, SearchModifier::Reverse) => { SearchModifier::SequentialReverse } (SearchModifier::Reverse, SearchModifier::Sequential) => { SearchModifier::ReverseSequential } (_, _) => unreachable!(), })); } return Ok(Some(m1)); }; Ok(None) } fn parse_result_trans(context: &mut ParseContext) -> Result, ParseError> { let maybe_mod = |token| match token { Token::Complement => Ok(Some(ResultTransform::Complement)), Token::Conditional => Ok(Some(ResultTransform::Conditional)), _ => Ok(None), }; let token = context.current()?; if let Some(m1) = maybe_mod(token)? { context.advance(1, true)?; if let Some(m2) = maybe_mod(context.current()?)? { context.advance(1, true)?; return Ok(Some(match (m1, m2) { (ResultTransform::Complement, ResultTransform::Conditional) => { ResultTransform::ComplementConditional } (ResultTransform::Conditional, ResultTransform::Complement) => { ResultTransform::ConditionalComplement } (_, _) => unreachable!(), })); } return Ok(Some(m1)); }; Ok(None) } fn parse_builtin_function_s(context: &mut ParseContext) -> Result { let token = context.current()?; let result = match token { Token::Empty => BuiltinSelectionFn::Empty, Token::EndOfBuffer => BuiltinSelectionFn::EndOfBuffer, Token::Span => BuiltinSelectionFn::Span, Token::CharacterOffset => return parse_char_offset(context), Token::Number(n) => BuiltinSelectionFn::Line(n), Token::Regex(pattern) => BuiltinSelectionFn::Regex(pattern), Token::AllMatches => return parse_all_matches(context), Token::LineOffset => return parse_relative_line(context), Token::BufferMatch => return parse_buffer_match(context), _ => { return Err(ParseError::UnexpectedToken { found: token, expecting: "selection function", }); } }; context.advance(1, false)?; Ok(result) } fn parse_char_offset(context: &mut ParseContext) -> Result { context.advance(1, true)?; let token = context.current()?; context.advance(1, false)?; match token { Token::Number(n) => Ok(BuiltinSelectionFn::CharOffset(n)), _ => Err(ParseError::UnexpectedToken { found: token, expecting: "number", }), } } fn parse_all_matches(context: &mut ParseContext) -> Result { context.advance(1, true)?; let token = context.current()?; context.advance(1, false)?; match token { Token::Regex(pattern) => Ok(BuiltinSelectionFn::AllMatches(pattern)), _ => Err(ParseError::UnexpectedToken { found: token, expecting: "regex", }), } } fn parse_relative_line(context: &mut ParseContext) -> Result { context.advance(1, true)?; let token = context.current()?; context.advance(1, false)?; match token { Token::Number(n) => Ok(BuiltinSelectionFn::RelativeLine(n)), _ => Err(ParseError::UnexpectedToken { found: token, expecting: "number", }), } } fn parse_buffer_match(context: &mut ParseContext) -> Result { context.advance(1, true)?; let token = context.current()?; context.advance(1, false)?; match token { Token::Regex(pattern) => Ok(BuiltinSelectionFn::BufferMatch(pattern)), _ => Err(ParseError::UnexpectedToken { found: token, expecting: "regex", }), } } #[cfg(test)] mod tests { use super::*; use crate::token::tokenise; #[test] fn test_parse_nullary() { let input = "/foo/"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); assert!(matches!( result, Composite::SelectionFunction { func: BuiltinSelectionFn::Regex(_), search_mod: None, result_transform: None, } )); } #[test] fn test_parse_train_with_modifiers() { let input = "e ?'/foo/"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::Train2 { f, g } => { assert!(matches!( *f, Composite::SelectionFunction { func: BuiltinSelectionFn::Empty, search_mod: None, result_transform: None, } )); assert!(matches!( *g, Composite::SelectionFunction { func: BuiltinSelectionFn::Regex(_), search_mod: Some(SearchModifier::Reverse), result_transform: Some(ResultTransform::Conditional), } )); } _ => panic!(), } } #[test] fn test_parse_nullary_with_modifier() { let input = "'e"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::SelectionFunction { func: BuiltinSelectionFn::Empty, search_mod: Some(SearchModifier::Reverse), result_transform: None, } => {} _ => panic!(), } } #[test] fn test_parse_2train() { let input = "- $"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::Train2 { f, g } => { assert!(matches!( *f, Composite::SelectionFunction { func: BuiltinSelectionFn::Span, search_mod: None, result_transform: None, } )); assert!(matches!( *g, Composite::SelectionFunction { func: BuiltinSelectionFn::EndOfBuffer, search_mod: None, result_transform: None, } )); } _ => panic!(), } } #[test] fn test_parse_3train() { let input = "x/start/ - x/end/"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::Train3 { f, g, h } => { match *f { Composite::SelectionFunction { func: BuiltinSelectionFn::AllMatches(pattern), search_mod: None, result_transform: None, } => assert_eq!(pattern, "start"), _ => panic!(), } assert!(matches!( *g, Composite::SelectionFunction { func: BuiltinSelectionFn::Span, search_mod: None, result_transform: None, } )); match *h { Composite::SelectionFunction { func: BuiltinSelectionFn::AllMatches(pattern), search_mod: None, result_transform: None, } => assert_eq!(pattern, "end"), _ => panic!(), } } _ => panic!(), } } #[test] fn test_parse_parenthesized() { let input = "(- $)"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::Train2 { f, g } => { assert!(matches!( *f, Composite::SelectionFunction { func: BuiltinSelectionFn::Span, search_mod: None, result_transform: None, } )); assert!(matches!( *g, Composite::SelectionFunction { func: BuiltinSelectionFn::EndOfBuffer, search_mod: None, result_transform: None, } )); } _ => panic!(), } } #[test] fn test_parse_5train() { let input = "#0 - #10 - #20"; let tokens = tokenise(input).unwrap(); let result = parse(tokens).unwrap(); println!("{:?}", result); match result { Composite::Train3 { f, g, h } => { match *f { Composite::SelectionFunction { func: BuiltinSelectionFn::CharOffset(n), search_mod: None, result_transform: None, } => assert_eq!(n, 0), _ => panic!(), } assert!(matches!( *g, Composite::SelectionFunction { func: BuiltinSelectionFn::Span, search_mod: None, result_transform: None, } )); match *h { Composite::Train3 { ref f, ref g, ref h, } => { match **f { Composite::SelectionFunction { func: BuiltinSelectionFn::CharOffset(n), search_mod: None, result_transform: None, } => assert_eq!(n, 10), _ => panic!(), } assert!(matches!( **g, Composite::SelectionFunction { func: BuiltinSelectionFn::Span, search_mod: None, result_transform: None, } )); match **h { Composite::SelectionFunction { func: BuiltinSelectionFn::CharOffset(n), search_mod: None, result_transform: None, } => assert_eq!(n, 20), _ => panic!(), } } _ => panic!(), } } _ => panic!(), } } }