+= -= *= /= % ** float .lf
This commit is contained in:
parent
56a07748cd
commit
12cd2296d5
|
@ -3,4 +3,9 @@ name = "leaf"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
|
|
101
src/lexer.rs
101
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<ParseIntError> for LexError {
|
|||
Self::InvalidInteger(err)
|
||||
}
|
||||
}
|
||||
impl From<ParseFloatError> for LexError {
|
||||
fn from(err: ParseFloatError) -> Self {
|
||||
Self::InvalidFloat(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, LexError>;
|
||||
|
||||
|
@ -210,21 +233,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn lex_integer(&mut self) -> Result<Token> {
|
||||
fn lex_number(&mut self) -> Result<Token> {
|
||||
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<Token> {
|
||||
|
@ -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))),
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -10,7 +10,7 @@ pub mod util;
|
|||
#[derive(Debug)]
|
||||
pub enum Expr {
|
||||
// Data and variables
|
||||
Assignment(Box<Expr>, Box<Expr>),
|
||||
Assign(Box<Expr>, Box<Expr>),
|
||||
Literal(Literal),
|
||||
// Non-literal datatypes
|
||||
Block(Block),
|
||||
|
@ -38,6 +38,12 @@ pub enum Expr {
|
|||
Multiply(Box<Expr>, Box<Expr>),
|
||||
Divide(Box<Expr>, Box<Expr>),
|
||||
Exponent(Box<Expr>, Box<Expr>),
|
||||
Modulo(Box<Expr>, Box<Expr>),
|
||||
// Binary operations: arithmetic w/ assignment
|
||||
AddAssign(Box<Expr>, Box<Expr>),
|
||||
SubtractAssign(Box<Expr>, Box<Expr>),
|
||||
MultiplyAssign(Box<Expr>, Box<Expr>),
|
||||
DivideAssign(Box<Expr>, Box<Expr>),
|
||||
}
|
||||
|
||||
#[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!(),
|
||||
});
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue