From e2213a627b186b5e86d7457b772764e5a58d6ab1 Mon Sep 17 00:00:00 2001 From: mehbark Date: Fri, 2 Jan 2026 21:07:26 -0500 Subject: [PATCH] parse math expressions --- src/ast.rs | 15 ++++++++++++++- src/parser.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/val.rs | 20 +++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 0c6b40a..3adfed3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -8,6 +8,19 @@ pub enum Ident { Return(Symbol), } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum BinOp { + Add, + Sub, + Mul, + Div, + Mod, + Pow, + Eq, + Lt, + Le, +} + // TODO: great display repr for this for testing (yes it's allowed) #[derive(Debug, Clone)] pub enum Ast { @@ -26,7 +39,7 @@ pub enum Ast { /// `f x` App(Box, Box), /// `x + y` (only builtins, sorry) - BinOp(Box, fn(Val, Val) -> Val, Box), + BinOp(Box, BinOp, Box), /// `3` Num(f64), /// `(println bla; 3) #=> 3` diff --git a/src/parser.rs b/src/parser.rs index 3860b3c..b6b7344 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,10 +3,11 @@ use std::{fmt, process}; use ariadne::{Color, Label, Report, ReportKind, Source}; use chumsky::{ input::{Stream, ValueInput}, + pratt::{infix, left, none, right}, prelude::*, }; -use crate::ast::{Ast, Ident}; +use crate::ast::{Ast, BinOp, Ident}; use logos::Logos; @@ -47,7 +48,7 @@ enum Token<'a> { #[token("fn")] Fn, - #[regex(r"[+*/%=-]")] + #[regex(r"([+*/%=<>-]|\*\*|<=|>=)")] BinOp(&'a str), #[regex(r"[a-zA-Z_][a-zA-Z_0-9']*")] @@ -210,7 +211,45 @@ where }) .labelled("function application"); - app.or(non_app) + let atom = app.or(non_app); + + // % + atom.pratt(( + infix(none(10), just(Token::BinOp("=")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Eq, Box::new(r)) + }), + infix(none(10), just(Token::BinOp("<")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Lt, Box::new(r)) + }), + infix(none(10), just(Token::BinOp("<=")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Le, Box::new(r)) + }), + // note that these two are flipped + infix(none(10), just(Token::BinOp(">")), |l, _, r, _| { + Ast::BinOp(Box::new(r), BinOp::Lt, Box::new(l)) + }), + infix(none(10), just(Token::BinOp(">=")), |l, _, r, _| { + Ast::BinOp(Box::new(r), BinOp::Le, Box::new(l)) + }), + infix(right(3), just(Token::BinOp("%")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Mod, Box::new(r)) + }), + infix(right(3), just(Token::BinOp("**")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Pow, Box::new(r)) + }), + infix(left(2), just(Token::BinOp("*")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Mul, Box::new(r)) + }), + infix(right(2), just(Token::BinOp("/")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Div, Box::new(r)) + }), + infix(left(1), just(Token::BinOp("+")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Add, Box::new(r)) + }), + infix(left(1), just(Token::BinOp("-")), |l, _, r, _| { + Ast::BinOp(Box::new(l), BinOp::Sub, Box::new(r)) + }), + )) }) .labelled("expression") .separated_by(semicolon) diff --git a/src/val.rs b/src/val.rs index e28584e..12fb230 100644 --- a/src/val.rs +++ b/src/val.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, ops}; use crate::{map::Map, symbol::Symbol}; @@ -23,3 +23,21 @@ impl fmt::Debug for Val { } } } + +impl ops::Add for Val { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Val::Number(a), Val::Number(b)) => Val::Number(a + b), + (f @ Val::Function { .. }, g @ Val::Function { .. }) => todo!(), + (Val::Map(mut m1), Val::Map(m2)) => { + for (k, v) in m2 { + m1.insert(k, v); + } + Val::Map(m1) + } + (lhs, rhs) => panic!("how do i add these {lhs:?} {rhs:?}"), + } + } +}