+= -= *= /= % ** float .lf
This commit is contained in:
parent
56a07748cd
commit
12cd2296d5
|
@ -3,4 +3,9 @@ name = "leaf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
[dependencies]
|
[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;
|
use crate::kinds;
|
||||||
|
|
||||||
|
@ -14,6 +18,7 @@ impl fmt::Display for Ident {
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
String(String),
|
String(String),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
|
Float(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Nil,
|
Nil,
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
|
@ -23,6 +28,7 @@ impl fmt::Display for Literal {
|
||||||
match self {
|
match self {
|
||||||
Literal::String(s) => write!(f, "\"{s}\""),
|
Literal::String(s) => write!(f, "\"{s}\""),
|
||||||
Literal::Integer(n) => write!(f, "{n}"),
|
Literal::Integer(n) => write!(f, "{n}"),
|
||||||
|
Literal::Float(n) => write!(f, "{n}"),
|
||||||
Literal::Boolean(b) => write!(f, "{b}"),
|
Literal::Boolean(b) => write!(f, "{b}"),
|
||||||
Literal::Ident(id) => write!(f, "{id}"),
|
Literal::Ident(id) => write!(f, "{id}"),
|
||||||
Literal::Nil => write!(f, "nil"),
|
Literal::Nil => write!(f, "nil"),
|
||||||
|
@ -39,7 +45,11 @@ kinds!(
|
||||||
Star,
|
Star,
|
||||||
Slash,
|
Slash,
|
||||||
Percent,
|
Percent,
|
||||||
Caret,
|
StarStar,
|
||||||
|
PlusEquals,
|
||||||
|
MinusEquals,
|
||||||
|
StarEquals,
|
||||||
|
SlashEquals,
|
||||||
CurlyOpen,
|
CurlyOpen,
|
||||||
CurlyClose,
|
CurlyClose,
|
||||||
ParenOpen,
|
ParenOpen,
|
||||||
|
@ -65,11 +75,12 @@ kinds!(
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Min,
|
Min,
|
||||||
Assign,
|
Assign,
|
||||||
|
WithAssign,
|
||||||
Logical,
|
Logical,
|
||||||
Equality,
|
Equality,
|
||||||
Relational,
|
Relational,
|
||||||
AddSub,
|
AddSub,
|
||||||
MulDiv,
|
MulDivMod,
|
||||||
Pow,
|
Pow,
|
||||||
Prefix,
|
Prefix,
|
||||||
}
|
}
|
||||||
|
@ -96,9 +107,14 @@ impl Token {
|
||||||
| Token::GreaterThanOrEqualTo => (Precedence::Relational, Associativity::Left),
|
| Token::GreaterThanOrEqualTo => (Precedence::Relational, Associativity::Left),
|
||||||
Token::And | Token::Or => (Precedence::Logical, Associativity::Left),
|
Token::And | Token::Or => (Precedence::Logical, Associativity::Left),
|
||||||
Token::Plus | Token::Minus => (Precedence::AddSub, Associativity::Left),
|
Token::Plus | Token::Minus => (Precedence::AddSub, Associativity::Left),
|
||||||
Token::Star | Token::Slash => (Precedence::MulDiv, Associativity::Left),
|
Token::Star | Token::Slash | Token::Percent => {
|
||||||
Token::Caret => (Precedence::Pow, Associativity::Right),
|
(Precedence::MulDivMod, Associativity::Left)
|
||||||
|
}
|
||||||
|
Token::StarStar => (Precedence::Pow, Associativity::Right),
|
||||||
Token::Equals => (Precedence::Assign, Associativity::Right),
|
Token::Equals => (Precedence::Assign, Associativity::Right),
|
||||||
|
Token::PlusEquals | Token::MinusEquals | Token::StarEquals | Token::SlashEquals => {
|
||||||
|
(Precedence::WithAssign, Associativity::Right)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -107,6 +123,7 @@ impl Token {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LexError {
|
pub enum LexError {
|
||||||
InvalidInteger(ParseIntError),
|
InvalidInteger(ParseIntError),
|
||||||
|
InvalidFloat(ParseFloatError),
|
||||||
InvalidEscape(char),
|
InvalidEscape(char),
|
||||||
UnexpectedCharacter(char),
|
UnexpectedCharacter(char),
|
||||||
UnexpectedEnd,
|
UnexpectedEnd,
|
||||||
|
@ -115,6 +132,7 @@ impl fmt::Display for LexError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::InvalidInteger(err) => write!(f, "invalid integer: {err}"),
|
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::UnexpectedEnd => write!(f, "unexpected end of source"),
|
||||||
Self::UnexpectedCharacter(c) => write!(f, "unexpected char '{c}'"),
|
Self::UnexpectedCharacter(c) => write!(f, "unexpected char '{c}'"),
|
||||||
Self::InvalidEscape(c) => write!(f, "\"\\{c}\" is not a valid string escape"),
|
Self::InvalidEscape(c) => write!(f, "\"\\{c}\" is not a valid string escape"),
|
||||||
|
@ -126,6 +144,11 @@ impl From<ParseIntError> for LexError {
|
||||||
Self::InvalidInteger(err)
|
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>;
|
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();
|
let mut n_str = String::new();
|
||||||
|
|
||||||
// we don't lex negatives. the impl for that is
|
// we don't lex negatives. the impl for that is
|
||||||
// a negation of a positive number at runtime.
|
// a negation of a positive number at runtime.
|
||||||
// maybe that's kind of stupid though, lol
|
// 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());
|
n_str.push(self.next_unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can only read digits 0 to 9 so this should not fail
|
let lit = if is_float {
|
||||||
// .. unless we overflow
|
Literal::Float(n_str.parse()?)
|
||||||
let n = 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> {
|
fn lex_string(&mut self) -> Result<Token> {
|
||||||
|
@ -264,23 +293,47 @@ where
|
||||||
')' => self.eat_to(Token::ParenClose),
|
')' => self.eat_to(Token::ParenClose),
|
||||||
|
|
||||||
// + add
|
// + add
|
||||||
'+' => self.eat_to(Token::Plus),
|
// or += add eq
|
||||||
|
'+' => match self.eat_peek() {
|
||||||
|
Some('=') => self.eat_to(Token::PlusEquals),
|
||||||
|
_ => t(Token::Plus),
|
||||||
|
},
|
||||||
|
|
||||||
// - subtract
|
// - subtract
|
||||||
'-' => self.eat_to(Token::Minus),
|
// or -= sub eq
|
||||||
|
'-' => match self.eat_peek() {
|
||||||
|
Some('=') => self.eat_to(Token::MinusEquals),
|
||||||
|
_ => t(Token::Minus),
|
||||||
|
},
|
||||||
|
|
||||||
// * multiply
|
// * 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
|
// / 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
|
// % modulo
|
||||||
'%' => self.eat_to(Token::Percent),
|
'%' => self.eat_to(Token::Percent),
|
||||||
|
|
||||||
// ^ pow
|
|
||||||
'^' => self.eat_to(Token::Caret),
|
|
||||||
|
|
||||||
// , comma
|
// , comma
|
||||||
',' => self.eat_to(Token::Comma),
|
',' => self.eat_to(Token::Comma),
|
||||||
|
|
||||||
|
@ -322,21 +375,11 @@ where
|
||||||
'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())),
|
'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())),
|
||||||
|
|
||||||
// 0-9 integer
|
// 0-9 integer
|
||||||
'0'..='9' => Some(self.lex_integer()),
|
'0'..='9' | '.' => Some(self.lex_number()),
|
||||||
|
|
||||||
// " strings
|
// " strings
|
||||||
'"' => Some(self.lex_string()),
|
'"' => 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
|
// unexpected character
|
||||||
c => Some(Err(LexError::UnexpectedCharacter(c))),
|
c => Some(Err(LexError::UnexpectedCharacter(c))),
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
fn main() {
|
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 lexer = Lexer::new(script.chars());
|
||||||
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub mod util;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Data and variables
|
// Data and variables
|
||||||
Assignment(Box<Expr>, Box<Expr>),
|
Assign(Box<Expr>, Box<Expr>),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
// Non-literal datatypes
|
// Non-literal datatypes
|
||||||
Block(Block),
|
Block(Block),
|
||||||
|
@ -38,6 +38,12 @@ pub enum Expr {
|
||||||
Multiply(Box<Expr>, Box<Expr>),
|
Multiply(Box<Expr>, Box<Expr>),
|
||||||
Divide(Box<Expr>, Box<Expr>),
|
Divide(Box<Expr>, Box<Expr>),
|
||||||
Exponent(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)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -245,13 +251,18 @@ where
|
||||||
// add, subtract
|
// add, subtract
|
||||||
Token::Plus => Expr::Add(lhs, rhs),
|
Token::Plus => Expr::Add(lhs, rhs),
|
||||||
Token::Minus => Expr::Subtract(lhs, rhs),
|
Token::Minus => Expr::Subtract(lhs, rhs),
|
||||||
|
Token::PlusEquals => Expr::AddAssign(lhs, rhs),
|
||||||
|
Token::MinusEquals => Expr::SubtractAssign(lhs, rhs),
|
||||||
// multiply, divide
|
// multiply, divide
|
||||||
Token::Star => Expr::Multiply(lhs, rhs),
|
Token::Star => Expr::Multiply(lhs, rhs),
|
||||||
Token::Slash => Expr::Divide(lhs, rhs),
|
Token::Slash => Expr::Divide(lhs, rhs),
|
||||||
// exponent
|
Token::StarEquals => Expr::MultiplyAssign(lhs, rhs),
|
||||||
Token::Caret => Expr::Exponent(lhs, rhs),
|
Token::SlashEquals => Expr::DivideAssign(lhs, rhs),
|
||||||
|
// exponent, modulo
|
||||||
|
Token::StarStar => Expr::Exponent(lhs, rhs),
|
||||||
|
Token::Percent => Expr::Modulo(lhs, rhs),
|
||||||
// assignment
|
// assignment
|
||||||
Token::Equals => Expr::Assignment(lhs, rhs),
|
Token::Equals => Expr::Assign(lhs, rhs),
|
||||||
// unreachable as all tokens with precedences are covered above
|
// unreachable as all tokens with precedences are covered above
|
||||||
_ => unreachable!(),
|
_ => 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 {
|
fn fmt_expr(e: Expr, depth: usize) -> String {
|
||||||
match e {
|
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::Literal(l) => l.to_string(),
|
||||||
Expr::Call(l, r) => {
|
Expr::Call(l, r) => {
|
||||||
let mut result = fmt_expr(*l, depth);
|
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::Subtract(l, r) => fmt_binop(*l, *r, "-", depth),
|
||||||
Expr::Multiply(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::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