Start implementing parser

- Implemented AST + parsing for maths expressions
This commit is contained in:
Daniel M 2021-12-23 22:59:57 +01:00
parent f2a00e6560
commit 8de6e990b4
5 changed files with 265 additions and 12 deletions

View File

@ -4,14 +4,7 @@ use plang2_lib::*;
fn main() { fn main() {
let code = r#" let code = r#"
// This is the main function -( -5 + 2 ) * -( 2 * -5 ) + -( 2 - 6 )
fn main() {
let a = 5465;
let b = 8;
let c = a + b;
print_int(c);
}
"#; "#;
let mut lexer = Lexer::new(code); let mut lexer = Lexer::new(code);
@ -20,4 +13,9 @@ fn main() {
println!("Tokens: \n{}\n", tokens); println!("Tokens: \n{}\n", tokens);
let mut parser = Parser::new(tokens);
let expr = parser.parse().unwrap();
println!("{:#?}", expr);
} }

25
plang2_lib/src/ast.rs Normal file
View File

@ -0,0 +1,25 @@
use crate::token::Literal;
/// Binary Operator Types. For operations that have two operands
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum BinOpType {
Add,
Sub,
Mul,
Div,
Mod
}
/// Unary Operator Types. For operations that have one operand
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UnOpType {
Neg
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
Literal(Literal),
BinOp(BinOpType, Box<Expr>, Box<Expr>),
UnOp(UnOpType, Box<Expr>),
}

View File

@ -1,5 +1,7 @@
pub mod token; pub mod token;
pub mod lexer; pub mod lexer;
pub mod ast;
pub mod parser;
pub use token::*; pub use lexer::Lexer;
pub use lexer::*; pub use parser::Parser;

225
plang2_lib/src/parser.rs Normal file
View File

@ -0,0 +1,225 @@
use crate::{
ast::{BinOpType, Expr, 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 | Literal
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_literal()?,
};
Ok(term)
}
pub fn parse_expr_literal(&mut self) -> PRes<Expr> {
match self.advance() {
Some(Token::Literal(lit)) => Ok(Expr::Literal(lit.clone())),
_ => panic!("Unexpected token. Expected literal"),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
ast::{BinOpType, Expr, UnOpType},
token::{Group, Literal, Op, Token, TokenStream},
Parser,
};
// fn parse_str(code: &str) -> Expr {
// let mut lexer = Lexer::new(code);
// let tokens = lexer.tokenize().unwrap();
// let mut parser = Parser::new(tokens);
// parser.parse().unwrap()
// }
#[test]
fn test_groupings_neg() {
// let input = "(-(-5+2)*-(2*-5)+-(2-6)) % 30";
// (-(-5+2)*-(2*-5)+-(2-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)),
Token::Op(Op::Add),
Token::Literal(Literal::Int64(2)),
Token::Close(Group::Paren),
Token::Op(Op::Mul),
Token::Op(Op::Sub),
Token::Open(Group::Paren),
Token::Literal(Literal::Int64(2)),
Token::Op(Op::Mul),
Token::Op(Op::Sub),
Token::Literal(Literal::Int64(5)),
Token::Close(Group::Paren),
Token::Op(Op::Add),
Token::Op(Op::Sub),
Token::Open(Group::Paren),
Token::Literal(Literal::Int64(2)),
Token::Op(Op::Sub),
Token::Literal(Literal::Int64(6)),
Token::Close(Group::Paren),
Token::Close(Group::Paren),
Token::Op(Op::Mod),
Token::Literal(Literal::Int64(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*-5)
let neg_grp_2_mul_neg_2 = Expr::UnOp(
UnOpType::Neg,
Box::new(Expr::BinOp(
BinOpType::Mul,
Expr::Literal(Literal::Int64(2)).into(),
Box::new(Expr::UnOp(
UnOpType::Neg,
Expr::Literal(Literal::Int64(5)).into(),
)),
)),
);
// -(-5+2)*-(2*-5)
let mul_first = Expr::BinOp(
BinOpType::Mul,
neg_grp_neg_5_add_2.into(),
neg_grp_2_mul_neg_2.into(),
);
// -(2-6)
let neg_grp_2_sub_6 = Expr::UnOp(
UnOpType::Neg,
Box::new(Expr::BinOp(
BinOpType::Sub,
Expr::Literal(Literal::Int64(2)).into(),
Expr::Literal(Literal::Int64(6)).into(),
)),
);
// -(-5+2)*-(2*-5)+-(2-6)
let left_of_mod = Expr::BinOp(BinOpType::Add, mul_first.into(), neg_grp_2_sub_6.into());
// (-(-5+2)*-(2*-5)+-(2-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);
}
}

View File

@ -154,8 +154,11 @@ impl TokenStream {
pub fn peek(&self) -> Option<&Token> { pub fn peek(&self) -> Option<&Token> {
self.tokens.get(self.idx + 1) self.tokens.get(self.idx + 1)
} }
pub fn advance(&mut self) {
self.idx += 1 /// Advance to the next token. Sets curr to next and returns the old curr.
pub fn advance(&mut self) -> Option<&Token> {
self.idx += 1;
self.tokens.get(self.idx - 1)
} }
} }