From 12cd2296d59c995016194bf2e93570160e792708 Mon Sep 17 00:00:00 2001 From: minish Date: Mon, 21 Jul 2025 01:48:25 -0400 Subject: [PATCH] += -= *= /= % ** float .lf --- Cargo.toml | 5 +++ src/lexer.rs | 101 ++++++++++++++++++++++++++++++++------------- src/main.rs | 2 +- src/parser.rs | 19 +++++++-- src/parser/util.rs | 9 +++- 5 files changed, 100 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9be7182..d60c6db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,9 @@ name = "leaf" version = "0.1.0" edition = "2024" +[profile.release] +strip = true +lto = true +codegen-units = 1 + [dependencies] diff --git a/src/lexer.rs b/src/lexer.rs index c0d13bc..922321b 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,4 +1,8 @@ -use std::{fmt, iter::Peekable, num::ParseIntError}; +use std::{ + fmt, + iter::Peekable, + num::{ParseFloatError, ParseIntError}, +}; use crate::kinds; @@ -14,6 +18,7 @@ impl fmt::Display for Ident { pub enum Literal { String(String), Integer(i64), + Float(f64), Boolean(bool), Nil, Ident(Ident), @@ -23,6 +28,7 @@ impl fmt::Display for Literal { match self { Literal::String(s) => write!(f, "\"{s}\""), Literal::Integer(n) => write!(f, "{n}"), + Literal::Float(n) => write!(f, "{n}"), Literal::Boolean(b) => write!(f, "{b}"), Literal::Ident(id) => write!(f, "{id}"), Literal::Nil => write!(f, "nil"), @@ -39,7 +45,11 @@ kinds!( Star, Slash, Percent, - Caret, + StarStar, + PlusEquals, + MinusEquals, + StarEquals, + SlashEquals, CurlyOpen, CurlyClose, ParenOpen, @@ -65,11 +75,12 @@ kinds!( pub enum Precedence { Min, Assign, + WithAssign, Logical, Equality, Relational, AddSub, - MulDiv, + MulDivMod, Pow, Prefix, } @@ -96,9 +107,14 @@ impl Token { | Token::GreaterThanOrEqualTo => (Precedence::Relational, Associativity::Left), Token::And | Token::Or => (Precedence::Logical, Associativity::Left), Token::Plus | Token::Minus => (Precedence::AddSub, Associativity::Left), - Token::Star | Token::Slash => (Precedence::MulDiv, Associativity::Left), - Token::Caret => (Precedence::Pow, Associativity::Right), + Token::Star | Token::Slash | Token::Percent => { + (Precedence::MulDivMod, Associativity::Left) + } + Token::StarStar => (Precedence::Pow, Associativity::Right), Token::Equals => (Precedence::Assign, Associativity::Right), + Token::PlusEquals | Token::MinusEquals | Token::StarEquals | Token::SlashEquals => { + (Precedence::WithAssign, Associativity::Right) + } _ => return None, }) } @@ -107,6 +123,7 @@ impl Token { #[derive(Debug)] pub enum LexError { InvalidInteger(ParseIntError), + InvalidFloat(ParseFloatError), InvalidEscape(char), UnexpectedCharacter(char), UnexpectedEnd, @@ -115,6 +132,7 @@ impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::InvalidInteger(err) => write!(f, "invalid integer: {err}"), + Self::InvalidFloat(err) => write!(f, "invalid float: {err}"), Self::UnexpectedEnd => write!(f, "unexpected end of source"), Self::UnexpectedCharacter(c) => write!(f, "unexpected char '{c}'"), Self::InvalidEscape(c) => write!(f, "\"\\{c}\" is not a valid string escape"), @@ -126,6 +144,11 @@ impl From for LexError { Self::InvalidInteger(err) } } +impl From for LexError { + fn from(err: ParseFloatError) -> Self { + Self::InvalidFloat(err) + } +} pub type Result = std::result::Result; @@ -210,21 +233,27 @@ where } } - fn lex_integer(&mut self) -> Result { + fn lex_number(&mut self) -> Result { let mut n_str = String::new(); // we don't lex negatives. the impl for that is // a negation of a positive number at runtime. // maybe that's kind of stupid though, lol - while let Some('0'..='9') = self.peek() { + let mut is_float = false; + while let Some('0'..='9' | '.') = self.peek() { + if self.peek() == Some('.') { + is_float = true; + } n_str.push(self.next_unwrap()); } - // we can only read digits 0 to 9 so this should not fail - // .. unless we overflow - let n = n_str.parse()?; + let lit = if is_float { + Literal::Float(n_str.parse()?) + } else { + Literal::Integer(n_str.parse()?) + }; - Ok(Token::Literal(Literal::Integer(n))) + Ok(Token::Literal(lit)) } fn lex_string(&mut self) -> Result { @@ -264,23 +293,47 @@ where ')' => self.eat_to(Token::ParenClose), // + add - '+' => self.eat_to(Token::Plus), + // or += add eq + '+' => match self.eat_peek() { + Some('=') => self.eat_to(Token::PlusEquals), + _ => t(Token::Plus), + }, // - subtract - '-' => self.eat_to(Token::Minus), + // or -= sub eq + '-' => match self.eat_peek() { + Some('=') => self.eat_to(Token::MinusEquals), + _ => t(Token::Minus), + }, // * multiply - '*' => self.eat_to(Token::Star), + // or *= mult eq + // or ** pow + '*' => match self.eat_peek() { + Some('=') => self.eat_to(Token::StarEquals), + Some('*') => self.eat_to(Token::StarStar), + _ => t(Token::Star), + }, // / divide - '/' => self.eat_to(Token::Slash), + // or /= div eq + // or // comment + '/' => match self.eat_peek() { + Some('=') => self.eat_to(Token::SlashEquals), + Some('/') => { + // skip the rest of the line + // this leaves the newline btw + while !matches!(self.peek(), Some('\n') | None) { + self.eat(); + } + continue; + } + _ => t(Token::Slash), + }, // % modulo '%' => self.eat_to(Token::Percent), - // ^ pow - '^' => self.eat_to(Token::Caret), - // , comma ',' => self.eat_to(Token::Comma), @@ -322,21 +375,11 @@ where 'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())), // 0-9 integer - '0'..='9' => Some(self.lex_integer()), + '0'..='9' | '.' => Some(self.lex_number()), // " strings '"' => Some(self.lex_string()), - // # comments - '#' => { - // skip the rest of the line - // this leaves the newline btw - while !matches!(self.peek(), Some('\n') | None) { - self.eat(); - } - continue; - } - // unexpected character c => Some(Err(LexError::UnexpectedCharacter(c))), }; diff --git a/src/main.rs b/src/main.rs index 1cb223b..b2437b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ mod lexer; mod parser; fn main() { - let script = std::fs::read_to_string("./start.leaf").unwrap(); + let script = std::fs::read_to_string("./start.lf").unwrap(); let lexer = Lexer::new(script.chars()); let mut parser = Parser::new(lexer.map(Result::unwrap)); let start = Instant::now(); diff --git a/src/parser.rs b/src/parser.rs index 5161ac1..4a55a67 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -10,7 +10,7 @@ pub mod util; #[derive(Debug)] pub enum Expr { // Data and variables - Assignment(Box, Box), + Assign(Box, Box), Literal(Literal), // Non-literal datatypes Block(Block), @@ -38,6 +38,12 @@ pub enum Expr { Multiply(Box, Box), Divide(Box, Box), Exponent(Box, Box), + Modulo(Box, Box), + // Binary operations: arithmetic w/ assignment + AddAssign(Box, Box), + SubtractAssign(Box, Box), + MultiplyAssign(Box, Box), + DivideAssign(Box, Box), } #[derive(Debug, Default)] @@ -245,13 +251,18 @@ where // add, subtract Token::Plus => Expr::Add(lhs, rhs), Token::Minus => Expr::Subtract(lhs, rhs), + Token::PlusEquals => Expr::AddAssign(lhs, rhs), + Token::MinusEquals => Expr::SubtractAssign(lhs, rhs), // multiply, divide Token::Star => Expr::Multiply(lhs, rhs), Token::Slash => Expr::Divide(lhs, rhs), - // exponent - Token::Caret => Expr::Exponent(lhs, rhs), + Token::StarEquals => Expr::MultiplyAssign(lhs, rhs), + Token::SlashEquals => Expr::DivideAssign(lhs, rhs), + // exponent, modulo + Token::StarStar => Expr::Exponent(lhs, rhs), + Token::Percent => Expr::Modulo(lhs, rhs), // assignment - Token::Equals => Expr::Assignment(lhs, rhs), + Token::Equals => Expr::Assign(lhs, rhs), // unreachable as all tokens with precedences are covered above _ => unreachable!(), }); diff --git a/src/parser/util.rs b/src/parser/util.rs index 9bf977b..0c46cfb 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -18,7 +18,11 @@ fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String { fn fmt_expr(e: Expr, depth: usize) -> String { match e { - Expr::Assignment(l, r) => fmt_binop(*l, *r, "=", depth), + Expr::Assign(l, r) => fmt_binop(*l, *r, "=", depth), + Expr::AddAssign(l, r) => fmt_binop(*l, *r, "+=", depth), + Expr::SubtractAssign(l, r) => fmt_binop(*l, *r, "-=", depth), + Expr::MultiplyAssign(l, r) => fmt_binop(*l, *r, "*=", depth), + Expr::DivideAssign(l, r) => fmt_binop(*l, *r, "/=", depth), Expr::Literal(l) => l.to_string(), Expr::Call(l, r) => { let mut result = fmt_expr(*l, depth); @@ -78,6 +82,7 @@ fn fmt_expr(e: Expr, depth: usize) -> String { Expr::Subtract(l, r) => fmt_binop(*l, *r, "-", depth), Expr::Multiply(l, r) => fmt_binop(*l, *r, "*", depth), Expr::Divide(l, r) => fmt_binop(*l, *r, "/", depth), - Expr::Exponent(l, r) => fmt_binop(*l, *r, "^", depth), + Expr::Exponent(l, r) => fmt_binop(*l, *r, "**", depth), + Expr::Modulo(l, r) => fmt_binop(*l, *r, "%", depth), } }