use crate::{
Result,
ast::{
ast::{Ast, AstInfix, InfixOperator},
parse::{Parser, Precedence},
},
bump::BumpBox,
diagnostic::AstError,
token::{operator::Operator, token::TokenKind},
};
impl<'bump> Parser<'bump> {
pub(crate) fn parse_infix(&mut self, left: Ast<'bump>) -> Result<AstInfix<'bump>> {
let precedence = self.current_precedence()?;
let operator = self.parse_infix_operator()?;
let right = match &operator {
InfixOperator::Call(token) => Ast::Tuple(self.parse_tuple_call(*token)?),
InfixOperator::As(_) => self.parse_node(Precedence::None)?,
_ => self.parse_node(precedence)?,
};
Ok(AstInfix {
token: *left.token(),
left: BumpBox::new_in(left, self.bump()),
operator,
right: BumpBox::new_in(right, self.bump()),
})
}
pub(crate) fn parse_infix_operator(&mut self) -> Result<InfixOperator<'bump>> {
let token = self.advance()?;
match token.kind {
TokenKind::Operator(operator) => match operator {
Operator::OpenParen => Ok(InfixOperator::Call(token)),
Operator::Plus => Ok(InfixOperator::Add(token)),
Operator::Minus => Ok(InfixOperator::Subtract(token)),
Operator::Asterisk => Ok(InfixOperator::Multiply(token)),
Operator::Slash => Ok(InfixOperator::Divide(token)),
Operator::Percent => Ok(InfixOperator::Rem(token)),
Operator::Equal => Ok(InfixOperator::Assign(token)),
Operator::DoubleEqual => Ok(InfixOperator::Equal(token)),
Operator::BangEqual => Ok(InfixOperator::NotEqual(token)),
Operator::LeftAngle => Ok(InfixOperator::LessThan(token)),
Operator::LeftAngleEqual => Ok(InfixOperator::LessThanEqual(token)),
Operator::RightAngle => Ok(InfixOperator::GreaterThan(token)),
Operator::RightAngleEqual => Ok(InfixOperator::GreaterThanEqual(token)),
Operator::Colon => Ok(InfixOperator::TypeAscription(token)),
Operator::Dot => Ok(InfixOperator::AccessTable(token)),
Operator::DoubleColon => Ok(InfixOperator::AccessNamespace(token)),
Operator::As => Ok(InfixOperator::As(token)),
Operator::And => Ok(InfixOperator::And(token)),
Operator::Or => Ok(InfixOperator::Or(token)),
Operator::Xor => Ok(InfixOperator::Xor(token)),
_ => Err(AstError::UnsupportedToken {
fragment: token.fragment.to_owned(),
}
.into()),
},
_ => Err(AstError::UnsupportedToken {
fragment: token.fragment.to_owned(),
}
.into()),
}
}
}
#[cfg(test)]
pub mod tests {
use std::ops::Deref;
use crate::{
ast::{
ast::{
Ast::{Infix, Literal},
AstInfix, AstLiteral, InfixOperator,
},
parse::parse,
},
bump::Bump,
token::tokenize,
};
#[test]
fn test_as_one() {
let bump = Bump::new();
let source = "1 as one";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let infix = result[0].first_unchecked().as_infix();
assert_eq!(infix.left.as_literal_number().value(), "1");
assert!(matches!(infix.operator, InfixOperator::As(_)));
assert_eq!(infix.right.as_identifier().text(), "one");
}
#[test]
fn test_as_a() {
let bump = Bump::new();
let source = "1 as a";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let infix = result[0].first_unchecked().as_infix();
assert_eq!(infix.left.as_literal_number().value(), "1");
assert!(matches!(infix.operator, InfixOperator::As(_)));
assert_eq!(infix.right.as_identifier().text(), "a");
}
#[test]
fn test_add() {
let bump = Bump::new();
let source = "1 + 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Add(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_cast_infix() {
let bump = Bump::new();
let source = "cast(-1, int1) < cast(1, int16)";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let AstInfix {
left,
operator,
right,
..
} = result[0].first_unchecked().as_infix();
assert!(matches!(operator, InfixOperator::LessThan(_)));
left.as_cast();
right.as_cast();
}
#[test]
fn test_subtract() {
let bump = Bump::new();
let source = "1 - 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Subtract(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_subtract_negative() {
let bump = Bump::new();
let source = "-1 -2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let AstInfix {
left,
operator,
right,
..
} = result[0].first_unchecked().as_infix();
let left = left.as_literal_number();
assert_eq!(left.value(), "-1");
assert!(matches!(operator, InfixOperator::Subtract(_)));
let right_number = right.as_literal_number();
assert_eq!(right_number.value(), "2");
}
#[test]
fn test_multiply() {
let bump = Bump::new();
let source = "1 * 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Multiply(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_divide() {
let bump = Bump::new();
let source = "1 / 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Divide(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_remainder() {
let bump = Bump::new();
let source = "1 % 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Rem(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_greater_than() {
let bump = Bump::new();
let source = "1 > 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::GreaterThan(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_greater_than_or_equal() {
let bump = Bump::new();
let source = "1 >= 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::GreaterThanEqual(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_less_than() {
let bump = Bump::new();
let source = "1 < 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::LessThan(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_less_than_or_equal() {
let bump = Bump::new();
let source = "1 <= 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::LessThanEqual(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_equal() {
let bump = Bump::new();
let source = "1 == 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::Equal(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
#[test]
fn test_not_equal() {
let bump = Bump::new();
let source = "1 != 2";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let result = parse(&bump, source, tokens).unwrap();
assert_eq!(result.len(), 1);
let Infix(AstInfix {
left,
operator,
right,
..
}) = result[0].first_unchecked()
else {
panic!()
};
let Literal(AstLiteral::Number(node)) = left.deref() else {
panic!()
};
assert_eq!(node.value(), "1");
assert!(matches!(operator, InfixOperator::NotEqual(_)));
let Literal(AstLiteral::Number(node)) = right.deref() else {
panic!()
};
assert_eq!(node.value(), "2");
}
}