diff --git a/src/eval.rs b/src/eval.rs new file mode 100644 index 0000000..7707b86 --- /dev/null +++ b/src/eval.rs @@ -0,0 +1,38 @@ +use crate::parser::{BinaryOp, Expr, UnaryOp}; + +pub struct Evaluator { + ast: Expr +} + +impl Evaluator { + pub fn new(ast: Expr) -> Self { + Self { + ast + } + } + + fn eval_expr(&self, expr: Expr) -> i64 { + match expr { + Expr::Number(n) => n, + Expr::Unary { op, right } => { + match op { + UnaryOp::Neg => -self.eval_expr(*right) + } + }, + Expr::Binary { op, left, right } => { + match op { + BinaryOp::Add => self.eval_expr(*left) + self.eval_expr(*right), + BinaryOp::Sub => self.eval_expr(*left) - self.eval_expr(*right), + BinaryOp::Mul => self.eval_expr(*left) * self.eval_expr(*right), + BinaryOp::Div => self.eval_expr(*left) / self.eval_expr(*right), + BinaryOp::Mod => self.eval_expr(*left) % self.eval_expr(*right) + } + }, + expr => panic!("can't eval expression: {expr:?}") + } + } + + pub fn eval(&self) -> i64 { + self.eval_expr(self.ast.clone()) + } +} \ No newline at end of file diff --git a/src/lexer.rs b/src/lexer.rs index d49eafd..0a2bc74 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,25 +1,4 @@ -#[derive(Debug, PartialEq)] -pub enum Token { - Number(i64), - Ident(String), - - // Operators - Add, - Sub, - Mul, - Div, - Mod, - - // Parentheses - LParen, - RParen, - LBrace, - RBrace, - LBracket, - RBracket, - - Semicolon -} +use crate::token::Token; pub struct Lexer { source: Vec, diff --git a/src/main.rs b/src/main.rs index 59ed221..4d61fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,12 @@ use std::{fs::File, io::Read}; +mod token; mod lexer; use lexer::Lexer; +mod parser; +use parser::Parser; +mod eval; +use eval::Evaluator; fn main() -> std::io::Result<()> { let args = std::env::args().collect::>(); @@ -13,10 +18,24 @@ 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!("AST: {:?}", parsed); + let eval = Evaluator::new(parsed); + println!("Eval: {:?}", eval.eval()); + } } } diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..9e26822 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,139 @@ +use crate::token::Token; + +#[derive(Debug, Clone)] +pub enum BinaryOp { + Add, + Sub, + Mul, + Div, + Mod +} + +#[derive(Debug, Clone)] +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, Clone)] +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 new file mode 100644 index 0000000..778263c --- /dev/null +++ b/src/token.rs @@ -0,0 +1,22 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Token { + Number(i64), + Ident(String), + + // Operators + Add, + Sub, + Mul, + Div, + Mod, + + // Parentheses + LParen, + RParen, + LBrace, + RBrace, + LBracket, + RBracket, + + Semicolon +} \ No newline at end of file