diff --git a/src/main.rs b/src/main.rs index 3422c42..79fb1d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ use std::{fs::File, io::Read}; mod token; mod lexer; use lexer::Lexer; +mod parser; +use parser::Parser; fn main() -> std::io::Result<()> { let args = std::env::args().collect::>(); @@ -14,10 +16,20 @@ fn main() -> std::io::Result<()> { let mut lexer = Lexer::new(&source); + let mut tokens = Vec::new(); + while let Some(tok) = lexer.next() { + print!("{tok:?}, "); + tokens.push(tok); + } + + println!("\n"); + + let mut parser = Parser::new(tokens); loop { - match lexer.next() { - Some(tok) => print!("{tok:?}, "), - None => break + let parsed = parser.parse(); + match parsed { + parser::Expr::EOL => break, + _ => println!("{:?}", parsed) } } diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..357d42b --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,139 @@ +use crate::token::Token; + +#[derive(Debug)] +pub enum BinaryOp { + Add, + Sub, + Mul, + Div, + Mod +} + +#[derive(Debug)] +pub enum UnaryOp { + Neg +} + +fn prefix_bp(op: &UnaryOp) -> u8 { + match op { + UnaryOp::Neg => 5 + } +} + +fn infix_bp(op: &BinaryOp) -> (u8, u8) { + match op { + BinaryOp::Add | BinaryOp::Sub => (1, 2), + BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => (3, 4) + } +} + +#[derive(Debug)] +pub enum Expr { + Number(i64), + Ident(String), + EOL, + + Binary { + op: BinaryOp, + left: Box, + right: Box + }, + + Unary { + op: UnaryOp, + right: Box + } +} + +pub struct Parser { + tokens: Vec, + pos: usize +} + +impl Parser { + pub fn new(tokens: Vec) -> Self { + Self { + tokens, + pos: 0 + } + } + + fn advance(&mut self) { + self.pos += 1; + } + + fn peek(&self) -> Option<&Token> { + self.tokens.get(self.pos) + } + + fn next(&mut self) -> Option { + let tok = self.peek().cloned(); + self.advance(); + tok + } + + fn parse_atom(&mut self) -> Expr { + match self.next() { + Some(Token::Number(n)) => Expr::Number(n), + Some(Token::Ident(id)) => Expr::Ident(id), + Some(Token::LParen) => { + let expr = self.parse_expr(0); + + match self.next() { + Some(Token::RParen) => expr, + _ => panic!("expected ')'") + } + }, + tok => panic!("unknown token: {tok:?}") + } + } + + fn parse_prefix(&mut self) -> Expr { + match self.peek() { + Some(Token::Sub) => { + self.advance(); + let op = UnaryOp::Neg; + + let bp = prefix_bp(&op); + let rhs = self.parse_expr(bp); + + Expr::Unary { op, right: Box::new(rhs) } + }, + _ => self.parse_atom() + } + } + + fn parse_expr(&mut self, min_bp: u8) -> Expr { + let mut lhs = self.parse_prefix(); + + loop { + let op = match self.peek() { + Some(Token::Add) => BinaryOp::Add, + Some(Token::Sub) => BinaryOp::Sub, + Some(Token::Mul) => BinaryOp::Mul, + Some(Token::Div) => BinaryOp::Div, + Some(Token::Mod) => BinaryOp::Mod, + _ => break + }; + + let (left_bp, right_bp) = infix_bp(&op); + + if left_bp < min_bp { + break; + } + + self.advance(); + + let rhs = self.parse_expr(right_bp); + + lhs = Expr::Binary { left: Box::new(lhs), op, right: Box::new(rhs) } + } + + lhs + } + + pub fn parse(&mut self) -> Expr { + if self.peek() == None {return Expr::EOL} + self.parse_expr(0) + } +} \ No newline at end of file diff --git a/src/token.rs b/src/token.rs index bc82a92..778263c 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Token { Number(i64), Ident(String),