use crate::{Error, expr::Expr, token::Token};
pub(crate) struct Parser {
tokens: Vec<Token>,
pos: usize,
}
impl Parser {
pub(crate) fn new(tokens: Vec<Token>) -> Self {
Self { tokens, pos: 0 }
}
pub(crate) fn parse_expr(&mut self) -> Result<Expr, Error> {
let item = self.parse_or_test()?;
let mut exprs = match item {
Expr::Or(exprs) => exprs,
_ => vec![item],
};
while self.peek() == Some(&Token::Or) {
self.advance(); let item = self.parse_or_test()?;
match item {
Expr::Or(items) => exprs.extend(items),
_ => exprs.push(item),
}
}
if exprs.len() == 1 {
Ok(exprs.into_iter().next().unwrap())
} else {
Ok(Expr::Or(exprs))
}
}
fn parse_or_test(&mut self) -> Result<Expr, Error> {
let item = self.parse_and_test()?;
let mut exprs = match item {
Expr::And(exprs) => exprs,
_ => vec![item],
};
while self.peek() == Some(&Token::And) {
self.advance(); let item = self.parse_and_test()?;
match item {
Expr::And(items) => exprs.extend(items),
_ => exprs.push(item),
}
}
if exprs.len() == 1 {
Ok(exprs.into_iter().next().unwrap())
} else {
Ok(Expr::And(exprs))
}
}
fn parse_and_test(&mut self) -> Result<Expr, Error> {
if self.peek() == Some(&Token::Not) {
self.advance(); let expr = self.parse_and_test()?;
return Ok(Expr::Not(Box::new(expr)));
}
self.parse_comparison()
}
fn parse_comparison(&mut self) -> Result<Expr, Error> {
let primary = self.parse_primary()?;
let operator = self.peek().cloned();
match operator {
Some(Token::Eq) | Some(Token::Gt) | Some(Token::Lt) | Some(Token::Ge)
| Some(Token::Le) | Some(Token::Ne) | Some(Token::In) => {
self.advance();
let right = self.parse_comparison()?;
Ok(match operator {
Some(Token::Eq) => Expr::Eq(Box::new(primary), Box::new(right)),
Some(Token::Gt) => Expr::Gt(Box::new(primary), Box::new(right)),
Some(Token::Lt) => Expr::Lt(Box::new(primary), Box::new(right)),
Some(Token::Ge) => Expr::Ge(Box::new(primary), Box::new(right)),
Some(Token::Le) => Expr::Le(Box::new(primary), Box::new(right)),
Some(Token::Ne) => Expr::Ne(Box::new(primary), Box::new(right)),
Some(Token::In) => Expr::In(Box::new(primary), Box::new(right)),
_ => {
let err_msg = format!("unexpected operator: {operator:?}");
return Err(Error::Parse(err_msg));
}
})
}
_ => {
Ok(primary)
}
}
}
fn parse_primary(&mut self) -> Result<Expr, Error> {
if let Some(Token::Ident(name)) = self.peek() {
let name = name.clone();
self.advance();
if self.peek() == Some(&Token::LParen) {
return self.parse_func_call(name);
} else {
let value = Expr::field_(name);
return self.parse_method_call_chain_or_field_access(value);
}
}
let value = self.parse_value()?;
self.parse_method_call_chain_or_field_access(value)
}
fn parse_value(&mut self) -> Result<Expr, Error> {
if self.peek() == Some(&Token::LBracket) {
return self.parse_array();
}
let token = self.peek().cloned();
if let Some(token) = token {
self.advance();
Ok(match token {
Token::Str(s) => Expr::str_(s),
Token::I64(i) => Expr::i64_(i),
Token::F64(f) => Expr::f64_(f),
Token::Bool(b) => Expr::bool_(b),
Token::Null => Expr::null_(),
Token::Ident(name) => Expr::field_(name),
Token::LParen => {
let expr = self.parse_expr()?;
if self.peek() != Some(&Token::RParen) {
return Err(Error::Parse("expected ')'".to_string()));
}
self.advance();
expr
}
_ => {
let err_msg = format!("parse value: unexpected token: {token:?}");
return Err(Error::Parse(err_msg));
}
})
} else {
let err_msg = "no more tokens".to_string();
Err(Error::Parse(err_msg))
}
}
fn parse_func_call(&mut self, func_name: String) -> Result<Expr, Error> {
if self.peek() != Some(&Token::LParen) {
return Err(Error::Parse("expected '('".to_string()));
}
self.advance();
let mut args = Vec::new();
if self.peek() == Some(&Token::RParen) {
self.advance();
return Ok(Expr::FuncCall(func_name, args));
}
args.push(self.parse_expr()?);
while self.peek() == Some(&Token::Comma) {
self.advance(); args.push(self.parse_expr()?);
}
if self.peek() != Some(&Token::RParen) {
return Err(Error::Parse("expected ')'".to_string()));
}
self.advance();
Ok(Expr::FuncCall(func_name, args))
}
fn parse_method_call_chain_or_field_access(&mut self, mut value: Expr) -> Result<Expr, Error> {
while self.peek() == Some(&Token::Dot) {
self.advance();
let method_or_field_name = match self.peek() {
Some(Token::Ident(name)) => {
let name = name.clone();
self.advance();
name
}
_ => {
return Err(Error::Parse("expected method name after '.'".to_string()));
}
};
if self.peek() != Some(&Token::LParen) {
value = Expr::FieldAccess(Box::new(value), method_or_field_name);
continue;
}
self.advance();
let mut args = Vec::new();
if self.peek() == Some(&Token::RParen) {
self.advance();
value = Expr::MethodCall(method_or_field_name, Box::new(value), args);
continue;
}
args.push(self.parse_value()?);
while self.peek() == Some(&Token::Comma) {
self.advance(); args.push(self.parse_value()?);
}
if self.peek() != Some(&Token::RParen) {
return Err(Error::Parse("expected ')'".to_string()));
}
self.advance();
value = Expr::MethodCall(method_or_field_name, Box::new(value), args);
}
Ok(value)
}
fn parse_array(&mut self) -> Result<Expr, Error> {
if self.peek() != Some(&Token::LBracket) {
return Err(Error::Parse("expected [".to_string()));
}
self.advance();
let mut values = Vec::new();
if self.peek() == Some(&Token::RBracket) {
self.advance();
return Ok(Expr::array_(vec![]));
}
values.push(self.parse_value()?);
while self.peek() == Some(&Token::Comma) {
self.advance(); values.push(self.parse_value()?);
}
if self.peek() != Some(&Token::RBracket) {
return Err(Error::Parse("expected ]".to_string()));
}
self.advance();
Ok(Expr::array_(values))
}
fn peek(&self) -> Option<&Token> {
self.tokens.get(self.pos)
}
fn advance(&mut self) {
if self.pos < self.tokens.len() {
self.pos += 1;
}
}
}
#[cfg(test)]
mod tests {
use crate::token::parse_token;
use super::*;
#[test]
fn test_parse_expr() {
let input = "name = 'John' AND age > 18 AND 1 > 0";
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::and_([
Expr::eq_(Expr::field_("name"), Expr::str_("John")),
Expr::gt_(Expr::field_("age"), Expr::i64_(18)),
Expr::gt_(Expr::i64_(1), Expr::i64_(0)),
])
);
let input = r#"name = "John" AND age IN [18, 19, 20, 22] AND 1 > 0"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::and_([
Expr::eq_(Expr::field_("name"), Expr::str_("John")),
Expr::in_(
Expr::field_("age"),
Expr::array_([
Expr::i64_(18),
Expr::i64_(19),
Expr::i64_(20),
Expr::i64_(22)
])
),
Expr::gt_(Expr::i64_(1), Expr::i64_(0)),
])
);
let input = r#"matches(name, "^J.*n$")"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::FuncCall(
"matches".to_string(),
vec![Expr::field_("name"), Expr::str_("^J.*n$"),]
)
);
let input = r#"name != null"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(expr, Expr::ne_(Expr::field_("name"), Expr::null_()));
let input = r#"name.to_uppercase() = 'JOHN'"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::eq_(
Expr::method_call_(Expr::field_("name"), "to_uppercase".to_string(), vec![]),
Expr::str_("JOHN")
)
);
let input = r#"name.to_uppercase().to_lowercase() = 'john'"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::eq_(
Expr::method_call_(
Expr::method_call_(Expr::field_("name"), "to_uppercase".to_string(), vec![]),
"to_lowercase".to_string(),
vec![]
),
Expr::str_("john")
)
);
let input = r#"name.contains('John')"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::method_call_(
Expr::field_("name"),
"contains".to_string(),
vec![Expr::str_("John")]
)
);
let input = r#"true OR false"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(expr, Expr::or_(vec![Expr::bool_(true), Expr::bool_(false)]));
let input = r#"true AND false OR true"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::or_(vec![
Expr::and_(vec![Expr::bool_(true), Expr::bool_(false)]),
Expr::bool_(true)
])
);
let input = r#"(true OR false) AND true"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::and_(vec![
Expr::or_(vec![Expr::bool_(true), Expr::bool_(false)]),
Expr::bool_(true)
])
);
let input = r#"(name = 'John' AND age > 18) AND 1 > 0"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::and_(vec![
Expr::eq_(Expr::field_("name"), Expr::str_("John")),
Expr::gt_(Expr::field_("age"), Expr::i64_(18)),
Expr::gt_(Expr::i64_(1), Expr::i64_(0))
])
);
let input = r#"name = 'John' AND (age > 18 AND 1 > 0)"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::and_(vec![
Expr::eq_(Expr::field_("name"), Expr::str_("John")),
Expr::gt_(Expr::field_("age"), Expr::i64_(18)),
Expr::gt_(Expr::i64_(1), Expr::i64_(0))
])
);
let input = r#"(name = 'John' AND age > 18) OR (1 > 0)"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::or_(vec![
Expr::and_(vec![
Expr::eq_(Expr::field_("name"), Expr::str_("John")),
Expr::gt_(Expr::field_("age"), Expr::i64_(18)),
]),
Expr::gt_(Expr::i64_(1), Expr::i64_(0))
])
);
let input = r#"type(maybe_i64_or_f64) IN ['i64', 'f64']"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::in_(
Expr::FuncCall("type".to_string(), vec![Expr::field_("maybe_i64_or_f64")]),
Expr::array_([Expr::str_("i64"), Expr::str_("f64")])
)
);
let input = r#"type(foo.contains('bar')) = 'i64'"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::eq_(
Expr::FuncCall(
"type".to_string(),
vec![Expr::method_call_(
Expr::field_("foo"),
"contains".to_string(),
vec![Expr::str_("bar")]
)]
),
Expr::str_("i64")
)
);
let input = r#"foo.bar.baz = 5"#;
let tokens = parse_token(input).unwrap();
let mut parser = Parser::new(tokens);
let expr = parser.parse_expr().unwrap();
assert_eq!(
expr,
Expr::eq_(
Expr::field_access_(Expr::field_access_(Expr::field_("foo"), "bar"), "baz"),
Expr::i64_(5),
)
)
}
}