Compare commits

...

3 commits

Author SHA1 Message Date
Raptorox
99be8a16c7
add evaluator 2026-05-21 17:12:06 +02:00
Raptorox
c70c5fab4d
add pratt parser 2026-05-21 16:59:07 +02:00
Raptorox
a4eed5214e
move Token out of lexer 2026-05-21 14:08:50 +02:00
5 changed files with 222 additions and 25 deletions

38
src/eval.rs Normal file
View file

@ -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())
}
}

View file

@ -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<u8>,

View file

@ -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::<Vec<String>>();
@ -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());
}
}
}

139
src/parser.rs Normal file
View file

@ -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<Expr>,
right: Box<Expr>
},
Unary {
op: UnaryOp,
right: Box<Expr>
}
}
pub struct Parser {
tokens: Vec<Token>,
pos: usize
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> 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<Token> {
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)
}
}

22
src/token.rs Normal file
View file

@ -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
}