use crate::internal::{ast::Term, errors::ParseError, lexer::Token};
pub fn parse_rhs<'a>(
tokens: &'a [(Token, &'a str)],
pos: &mut usize,
) -> Result<(Vec<Term>, bool), ParseError> {
let mut terms = Vec::new();
let mut has_intercept = true;
if crate::internal::peek::peek(tokens, *pos).is_some()
&& !matches!(
crate::internal::peek::peek(tokens, *pos).unwrap().0,
Token::Comma | Token::Plus
)
{
terms.push(crate::internal::parse_term::parse_term(tokens, pos)?);
}
while crate::internal::matches::matches(tokens, pos, |t| matches!(t, Token::Plus)) {
terms.push(crate::internal::parse_term::parse_term(tokens, pos)?);
}
if crate::internal::matches::matches(tokens, pos, |t| matches!(t, Token::Minus)) {
if crate::internal::matches::matches(tokens, pos, |t| matches!(t, Token::One)) {
has_intercept = false;
} else {
return Err(crate::internal::errors::ParseError::Syntax(
"expected '1' after '-' to remove intercept".into(),
));
}
}
Ok((terms, has_intercept))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::internal::lexer::Token;
#[test]
fn test_parse_rhs_single_term() {
let tokens = vec![(Token::ColumnName, "x")];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 1);
assert!(has_intercept);
}
#[test]
fn test_parse_rhs_multiple_terms() {
let tokens = vec![
(Token::ColumnName, "x"),
(Token::Plus, "+"),
(Token::ColumnName, "z"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 2);
assert!(has_intercept);
}
#[test]
fn test_parse_rhs_without_intercept() {
let tokens = vec![
(Token::ColumnName, "x"),
(Token::Minus, "-"),
(Token::One, "1"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 1);
assert!(!has_intercept);
}
#[test]
fn test_parse_rhs_empty() {
let tokens: Vec<(Token, &str)> = vec![];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 0);
assert!(has_intercept);
}
#[test]
fn test_parse_rhs_leading_plus() {
let tokens = vec![(Token::Plus, "+"), (Token::ColumnName, "x")];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 1); assert!(has_intercept);
assert_eq!(pos, 2); }
#[test]
fn test_parse_rhs_minus_without_one() {
let tokens = vec![
(Token::ColumnName, "x"),
(Token::Minus, "-"),
(Token::ColumnName, "y"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_err());
assert_eq!(pos, 2); }
#[test]
fn test_parse_rhs_multiple_plus_terms() {
let tokens = vec![
(Token::ColumnName, "x"),
(Token::Plus, "+"),
(Token::ColumnName, "y"),
(Token::Plus, "+"),
(Token::ColumnName, "z"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 3);
assert!(has_intercept);
}
#[test]
fn test_parse_rhs_with_function_terms() {
let tokens = vec![
(Token::Poly, "poly"),
(Token::FunctionStart, "("),
(Token::ColumnName, "x"),
(Token::Comma, ","),
(Token::Integer, "2"),
(Token::FunctionEnd, ")"),
(Token::Plus, "+"),
(Token::ColumnName, "z"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 2);
assert!(has_intercept);
}
#[test]
fn test_parse_rhs_stops_at_comma() {
let tokens = vec![
(Token::ColumnName, "x"),
(Token::Comma, ","),
(Token::Family, "family"),
];
let mut pos = 0;
let result = parse_rhs(&tokens, &mut pos);
assert!(result.is_ok());
let (terms, has_intercept) = result.unwrap();
assert_eq!(terms.len(), 1);
assert!(has_intercept);
assert_eq!(pos, 1); }
}