use super::Parser;
use crate::error::ParseError;
use crate::jia_lang::ast::{ArithOp, Expr};
use crate::jia_lang::lexer::TokenKind;
#[derive(Clone, Copy)]
enum UnaryExprFn {
StartOf,
EndOf,
DurationOf,
PresentOf,
}
impl UnaryExprFn {
fn from_name(name: &str) -> Option<Self> {
match name {
"start_of" => Some(Self::StartOf),
"end_of" => Some(Self::EndOf),
"duration_of" => Some(Self::DurationOf),
"present_of" => Some(Self::PresentOf),
_ => None,
}
}
fn apply(self, arg: String) -> Expr {
match self {
Self::StartOf => Expr::StartOf(arg),
Self::EndOf => Expr::EndOf(arg),
Self::DurationOf => Expr::DurationOf(arg),
Self::PresentOf => Expr::PresentOf(arg),
}
}
}
impl<'a> Parser<'a> {
pub fn parse_expr(&mut self) -> Result<Expr, ParseError> {
self.parse_additive()
}
fn parse_additive(&mut self) -> Result<Expr, ParseError> {
let mut left = self.parse_multiplicative()?;
loop {
let op = match self.peek_kind() {
Some(TokenKind::Plus) => ArithOp::Add,
Some(TokenKind::Minus) => ArithOp::Sub,
_ => break,
};
self.advance();
let right = self.parse_multiplicative()?;
left = Expr::BinaryOp {
op,
left: Box::new(left),
right: Box::new(right),
};
}
Ok(left)
}
fn parse_multiplicative(&mut self) -> Result<Expr, ParseError> {
let mut left = self.parse_atom()?;
loop {
let op = match self.peek_kind() {
Some(TokenKind::Star) => ArithOp::Mul,
Some(TokenKind::Slash) => ArithOp::Div,
_ => break,
};
self.advance();
let right = self.parse_atom()?;
left = Expr::BinaryOp {
op,
left: Box::new(left),
right: Box::new(right),
};
}
Ok(left)
}
fn parse_atom(&mut self) -> Result<Expr, ParseError> {
match self.peek_kind() {
Some(TokenKind::Number(n)) => {
self.advance();
Ok(Expr::Number(n))
}
Some(TokenKind::Float(f)) => {
self.advance();
Ok(Expr::Float(f))
}
Some(TokenKind::Minus) => {
self.advance();
let inner = self.parse_atom()?;
Ok(Expr::Negate(Box::new(inner)))
}
Some(TokenKind::LParen) => {
self.advance();
let expr = self.parse_expr()?;
self.expect_token(TokenKind::RParen)?;
Ok(expr)
}
Some(TokenKind::Ident(name)) => {
let name = name.clone();
if let Some(function) = UnaryExprFn::from_name(&name) {
self.advance();
self.expect_token(TokenKind::LParen)?;
let arg = self.expect_ident()?;
self.expect_token(TokenKind::RParen)?;
Ok(function.apply(arg))
} else {
self.advance();
Ok(Expr::Var(name))
}
}
_ => {
let span = self.current_span();
Err(ParseError::new("expected expression", span))
}
}
}
}
#[cfg(test)]
mod tests {
use crate::jia_lang::ast::{ArithOp, Expr};
use crate::jia_lang::lexer::tokenize;
use crate::jia_lang::parser::Parser;
fn parse_expr(input: &str) -> Expr {
let tokens = tokenize(input).unwrap();
let mut parser = Parser::new(&tokens);
parser.parse_expr().unwrap()
}
#[test]
fn test_number() {
assert_eq!(parse_expr("42"), Expr::Number(42));
}
#[test]
fn test_variable() {
assert_eq!(parse_expr("makespan"), Expr::Var("makespan".to_string()));
}
#[test]
fn test_start_of() {
assert_eq!(
parse_expr("start_of(task_a)"),
Expr::StartOf("task_a".to_string())
);
}
#[test]
fn test_end_of() {
assert_eq!(
parse_expr("end_of(task_b)"),
Expr::EndOf("task_b".to_string())
);
}
#[test]
fn test_duration_of() {
assert_eq!(
parse_expr("duration_of(task_a)"),
Expr::DurationOf("task_a".to_string())
);
}
#[test]
fn test_addition() {
assert_eq!(
parse_expr("end_of(a) + 5"),
Expr::BinaryOp {
op: ArithOp::Add,
left: Box::new(Expr::EndOf("a".to_string())),
right: Box::new(Expr::Number(5)),
}
);
}
#[test]
fn test_precedence_mul_before_add() {
let expr = parse_expr("1 + 2 * 3");
assert_eq!(
expr,
Expr::BinaryOp {
op: ArithOp::Add,
left: Box::new(Expr::Number(1)),
right: Box::new(Expr::BinaryOp {
op: ArithOp::Mul,
left: Box::new(Expr::Number(2)),
right: Box::new(Expr::Number(3)),
}),
}
);
}
#[test]
fn test_parenthesized() {
let expr = parse_expr("(1 + 2) * 3");
assert_eq!(
expr,
Expr::BinaryOp {
op: ArithOp::Mul,
left: Box::new(Expr::BinaryOp {
op: ArithOp::Add,
left: Box::new(Expr::Number(1)),
right: Box::new(Expr::Number(2)),
}),
right: Box::new(Expr::Number(3)),
}
);
}
#[test]
fn test_negation() {
assert_eq!(parse_expr("-5"), Expr::Negate(Box::new(Expr::Number(5))));
}
#[test]
fn test_present_of_and_expression_error() {
assert_eq!(
parse_expr("present_of(task_a)"),
Expr::PresentOf("task_a".to_string())
);
let tokens = tokenize(")").unwrap();
let mut parser = Parser::new(&tokens);
assert!(parser.parse_expr().is_err());
}
}