280 lines
8.6 KiB
Rust
280 lines
8.6 KiB
Rust
use crate::{
|
|
ast::{BinOpType, Expr, FnCall, UnOpType},
|
|
token::{Group, Op, Token, TokenStream},
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ParseErr;
|
|
|
|
type PRes<T> = Result<T, ParseErr>;
|
|
|
|
pub struct Parser {
|
|
tokens: TokenStream,
|
|
}
|
|
|
|
/*
|
|
GRAMMAR
|
|
|
|
expr_literal = Literal
|
|
expr_fn_call = FnCall
|
|
expr_varibale = Variable
|
|
expr_value = expr_literal | expr_fn_call | expr_variable
|
|
expr_term = "-" expr_term | "(" expr_add ")" | expr_literal
|
|
expr_mul = expr_term (("*"|"/") expr_term)*
|
|
expr_add = expr_mul (("+"|"-") expr_mul)*
|
|
*/
|
|
|
|
impl Parser {
|
|
pub fn new(tokens: TokenStream) -> Self {
|
|
Self { tokens }
|
|
}
|
|
|
|
pub fn curr(&self) -> Option<&Token> {
|
|
self.tokens.curr()
|
|
}
|
|
|
|
pub fn peek(&self) -> Option<&Token> {
|
|
self.tokens.peek()
|
|
}
|
|
|
|
pub fn advance(&mut self) -> Option<&Token> {
|
|
self.tokens.advance()
|
|
}
|
|
|
|
pub fn parse(&mut self) -> PRes<Expr> {
|
|
self.parse_expr_add()
|
|
}
|
|
|
|
pub fn parse_expr_add(&mut self) -> PRes<Expr> {
|
|
let mut a = self.parse_expr_mul()?;
|
|
|
|
while matches!(self.curr(), Some(Token::Op(Op::Add | Op::Sub))) {
|
|
// We successfully matched curr against Some already in the while condition, so unwrap is fine
|
|
let tok_op = self.advance().unwrap().clone();
|
|
|
|
let b = self.parse_expr_mul()?;
|
|
|
|
let op_type = match tok_op {
|
|
Token::Op(Op::Add) => BinOpType::Add,
|
|
Token::Op(Op::Sub) => BinOpType::Sub,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
a = Expr::BinOp(op_type, a.into(), b.into());
|
|
}
|
|
|
|
Ok(a)
|
|
}
|
|
|
|
pub fn parse_expr_mul(&mut self) -> PRes<Expr> {
|
|
let mut a = self.parse_expr_term()?;
|
|
|
|
while matches!(self.curr(), Some(Token::Op(Op::Mul | Op::Div | Op::Mod))) {
|
|
// We successfully matched curr against Some already in the while condition, so unwrap is fine
|
|
let tok_op = self.advance().unwrap().clone();
|
|
|
|
let b = self.parse_expr_term()?;
|
|
|
|
let op_type = match tok_op {
|
|
Token::Op(Op::Mul) => BinOpType::Mul,
|
|
Token::Op(Op::Div) => BinOpType::Div,
|
|
Token::Op(Op::Mod) => BinOpType::Mod,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
a = Expr::BinOp(op_type, a.into(), b.into());
|
|
}
|
|
|
|
Ok(a)
|
|
}
|
|
|
|
pub fn parse_expr_term(&mut self) -> PRes<Expr> {
|
|
let term = match self.curr() {
|
|
Some(Token::Open(Group::Paren)) => {
|
|
self.advance();
|
|
let a = self.parse_expr_add()?;
|
|
if !matches!(self.advance(), Some(Token::Close(Group::Paren))) {
|
|
panic!("Missing closing parentheses");
|
|
}
|
|
a
|
|
}
|
|
Some(Token::Op(Op::Sub)) => {
|
|
self.advance();
|
|
Expr::UnOp(UnOpType::Neg, self.parse_expr_term()?.into())
|
|
}
|
|
_ => self.parse_expr_value()?,
|
|
};
|
|
Ok(term)
|
|
}
|
|
|
|
pub fn parse_expr_value(&mut self) -> PRes<Expr> {
|
|
match self.curr() {
|
|
Some(Token::Literal(_)) => self.parse_expr_literal(),
|
|
Some(Token::Ident(_)) if matches!(self.peek(), Some(Token::Open(Group::Paren))) => {
|
|
self.parse_expr_fn_call()
|
|
}
|
|
Some(Token::Ident(_)) => self.parse_expr_varibale(),
|
|
_ => panic!("Expected value (literal, variable or function call)"),
|
|
}
|
|
}
|
|
|
|
pub fn parse_expr_fn_call(&mut self) -> PRes<Expr> {
|
|
// The first 2 checks are not really necessary for internal calls since parse_expr_value
|
|
// verifies the tokens already
|
|
let fn_name = match self.advance() {
|
|
Some(Token::Ident(ident)) => ident.clone(),
|
|
_ => panic!("Unexpected token while parsing function call. Expected identifier"),
|
|
};
|
|
|
|
if !matches!(self.advance(), Some(Token::Open(Group::Paren))) {
|
|
panic!("Unexpected token while parsing function call. Expected '('");
|
|
}
|
|
|
|
let mut args = Vec::new();
|
|
|
|
// TODO: This is *suboptimal* code
|
|
if !matches!(self.curr(), Some(Token::Close(Group::Paren))) {
|
|
args.push(self.parse_expr_add()?);
|
|
|
|
while matches!(self.curr(), Some(Token::Comma)) {
|
|
self.advance();
|
|
args.push(self.parse_expr_add()?);
|
|
}
|
|
}
|
|
|
|
if !matches!(self.advance(), Some(Token::Close(Group::Paren))) {
|
|
panic!("Unexpected token while parsing function call. Expected '('");
|
|
}
|
|
|
|
Ok(Expr::FnCall(FnCall { fn_name, args }))
|
|
}
|
|
|
|
pub fn parse_expr_varibale(&mut self) -> PRes<Expr> {
|
|
match self.advance() {
|
|
Some(Token::Ident(ident)) => Ok(Expr::Variable(ident.clone())),
|
|
_ => panic!("Unexpected token while parsing variable. Expected identifier"),
|
|
}
|
|
}
|
|
|
|
pub fn parse_expr_literal(&mut self) -> PRes<Expr> {
|
|
match self.advance() {
|
|
Some(Token::Literal(lit)) => Ok(Expr::Literal(lit.clone())),
|
|
_ => panic!("Unexpected token while parsing literal. Expected literal"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::{
|
|
ast::{BinOpType, Expr, FnCall, UnOpType},
|
|
token::{Group, Literal, Op, Token, TokenStream},
|
|
Parser,
|
|
};
|
|
|
|
#[test]
|
|
fn test_groupings_neg() {
|
|
// let input = "(-(-5+2)*-(2*-sqrt(9))+-(a-6)) % 30";
|
|
|
|
let fn_name = "sqrt".to_string();
|
|
let var_name = "a".to_string();
|
|
|
|
// (-(-5+2)*-(2*-sqrt(9))+-(a-6)) % 30
|
|
let input_toks = vec![
|
|
Token::Open(Group::Paren), // (
|
|
Token::Op(Op::Sub), // -
|
|
Token::Open(Group::Paren), // (
|
|
Token::Op(Op::Sub), // -
|
|
Token::Literal(Literal::Int64(5)), // 5
|
|
Token::Op(Op::Add), // +
|
|
Token::Literal(Literal::Int64(2)), // 2
|
|
Token::Close(Group::Paren), // )
|
|
Token::Op(Op::Mul), // *
|
|
Token::Op(Op::Sub), // -
|
|
Token::Open(Group::Paren), // (
|
|
Token::Literal(Literal::Int64(2)), // 2
|
|
Token::Op(Op::Mul), // *
|
|
Token::Op(Op::Sub), // -
|
|
Token::Ident(fn_name.clone()), // sqrt
|
|
Token::Open(Group::Paren), // (
|
|
Token::Literal(Literal::Int64(9)), // 9
|
|
Token::Close(Group::Paren), // )
|
|
Token::Close(Group::Paren), // )
|
|
Token::Op(Op::Add), // +
|
|
Token::Op(Op::Sub), // -
|
|
Token::Open(Group::Paren), // (
|
|
Token::Ident(var_name.clone()), // a
|
|
Token::Op(Op::Sub), // -
|
|
Token::Literal(Literal::Int64(6)), // 6
|
|
Token::Close(Group::Paren), // )
|
|
Token::Close(Group::Paren), // )
|
|
Token::Op(Op::Mod), // %
|
|
Token::Literal(Literal::Int64(30)), // 30
|
|
];
|
|
|
|
// -(-5+2)
|
|
let neg_grp_neg_5_add_2 = Expr::UnOp(
|
|
UnOpType::Neg,
|
|
Box::new(Expr::BinOp(
|
|
BinOpType::Add,
|
|
Box::new(Expr::UnOp(
|
|
UnOpType::Neg,
|
|
Expr::Literal(Literal::Int64(5)).into(),
|
|
)),
|
|
Expr::Literal(Literal::Int64(2)).into(),
|
|
)),
|
|
);
|
|
|
|
// -(2*-sqrt(9))
|
|
let neg_grp_2_mul_neg_sqrt = Expr::UnOp(
|
|
UnOpType::Neg,
|
|
Box::new(Expr::BinOp(
|
|
BinOpType::Mul,
|
|
Expr::Literal(Literal::Int64(2)).into(),
|
|
Box::new(Expr::UnOp(
|
|
UnOpType::Neg,
|
|
Expr::FnCall(FnCall {
|
|
fn_name,
|
|
args: vec![Expr::Literal(Literal::Int64(9))],
|
|
})
|
|
.into(),
|
|
)),
|
|
)),
|
|
);
|
|
|
|
// -(-5+2) * -(2*-sqrt(9))
|
|
let mul_first = Expr::BinOp(
|
|
BinOpType::Mul,
|
|
neg_grp_neg_5_add_2.into(),
|
|
neg_grp_2_mul_neg_sqrt.into(),
|
|
);
|
|
|
|
// -(a-6)
|
|
let neg_grp_a_sub_6 = Expr::UnOp(
|
|
UnOpType::Neg,
|
|
Box::new(Expr::BinOp(
|
|
BinOpType::Sub,
|
|
Expr::Variable(var_name).into(),
|
|
Expr::Literal(Literal::Int64(6)).into(),
|
|
)),
|
|
);
|
|
|
|
// -(-5+2)*-(2*-sqrt(9)) + -(a-6)
|
|
let left_of_mod = Expr::BinOp(BinOpType::Add, mul_first.into(), neg_grp_a_sub_6.into());
|
|
|
|
// (-(-5+2) * -(2*-sqrt(9)) + -(a-6)) % 30
|
|
let expected = Expr::BinOp(
|
|
BinOpType::Mod,
|
|
left_of_mod.into(),
|
|
Expr::Literal(Literal::Int64(30)).into(),
|
|
);
|
|
|
|
// let res = parse_str(input);
|
|
|
|
let mut parser = Parser::new(TokenStream::new(input_toks));
|
|
let res = parser.parse_expr_add().unwrap();
|
|
|
|
assert_eq!(expected, res);
|
|
}
|
|
}
|