use crate::internal::{ast::Term, errors::ParseError, lexer::Token};
pub fn parse_term<'a>(tokens: &'a [(Token, &'a str)], pos: &mut usize) -> Result<Term, ParseError> {
let (tok, name_slice) = crate::internal::expect::expect(
tokens,
pos,
|t| matches!(t, Token::Poly | Token::ColumnName),
"Poly or ColumnName",
)?;
if crate::internal::matches::matches(tokens, pos, |t| matches!(t, Token::FunctionStart)) {
let fname = match tok {
Token::Poly => "poly".to_string(),
Token::ColumnName => name_slice.to_string(),
_ => unreachable!(),
};
let args = crate::internal::parse_arg_list::parse_arg_list(tokens, pos)?;
crate::internal::expect::expect(tokens, pos, |t| matches!(t, Token::FunctionEnd), ")")?;
Ok(Term::Function { name: fname, args })
} else {
match tok {
Token::ColumnName => Ok(Term::Column(name_slice.to_string())),
Token::Poly => Err(ParseError::Syntax("expected '(' after 'poly'".into())),
_ => Err(ParseError::Unexpected {
expected: "term",
found: Some(tok),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::internal::lexer::Token;
#[test]
fn test_parse_term_simple_column() {
let tokens = vec![(Token::ColumnName, "x")];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Column(name) => assert_eq!(name, "x"),
_ => panic!("Expected column term"),
}
assert_eq!(pos, 1);
}
#[test]
fn test_parse_term_poly_function() {
let tokens = vec![
(Token::Poly, "poly"),
(Token::FunctionStart, "("),
(Token::ColumnName, "x"),
(Token::Comma, ","),
(Token::Integer, "2"),
(Token::FunctionEnd, ")"),
];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Function { name, args } => {
assert_eq!(name, "poly");
assert_eq!(args.len(), 2);
}
_ => panic!("Expected function term"),
}
assert_eq!(pos, 6);
}
#[test]
fn test_parse_term_custom_function() {
let tokens = vec![
(Token::ColumnName, "log"),
(Token::FunctionStart, "("),
(Token::ColumnName, "price"),
(Token::FunctionEnd, ")"),
];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Function { name, args } => {
assert_eq!(name, "log");
assert_eq!(args.len(), 1);
}
_ => panic!("Expected function term"),
}
assert_eq!(pos, 4);
}
#[test]
fn test_parse_term_poly_without_parentheses() {
let tokens = vec![(Token::Poly, "poly")];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_err());
assert_eq!(pos, 1); }
#[test]
fn test_parse_term_function_with_multiple_args() {
let tokens = vec![
(Token::ColumnName, "custom_func"),
(Token::FunctionStart, "("),
(Token::ColumnName, "x"),
(Token::Comma, ","),
(Token::ColumnName, "y"),
(Token::Comma, ","),
(Token::Integer, "10"),
(Token::FunctionEnd, ")"),
];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Function { name, args } => {
assert_eq!(name, "custom_func");
assert_eq!(args.len(), 3);
}
_ => panic!("Expected function term"),
}
assert_eq!(pos, 8);
}
#[test]
fn test_parse_term_function_without_closing_paren() {
let tokens = vec![
(Token::ColumnName, "func"),
(Token::FunctionStart, "("),
(Token::ColumnName, "x"),
];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_err());
assert_eq!(pos, 3); }
#[test]
fn test_parse_term_long_column_name() {
let tokens = vec![(Token::ColumnName, "very_long_column_name_with_underscores")];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Column(name) => assert_eq!(name, "very_long_column_name_with_underscores"),
_ => panic!("Expected column term"),
}
assert_eq!(pos, 1);
}
#[test]
fn test_parse_term_numeric_column_name() {
let tokens = vec![(Token::ColumnName, "x1")];
let mut pos = 0;
let result = parse_term(&tokens, &mut pos);
assert!(result.is_ok());
match result.unwrap() {
Term::Column(name) => assert_eq!(name, "x1"),
_ => panic!("Expected column term"),
}
assert_eq!(pos, 1);
}
}