From 6049299cab488247953e588b9d6d3e572110510a Mon Sep 17 00:00:00 2001 From: minish Date: Tue, 1 Jul 2025 18:21:52 -0400 Subject: [PATCH] function calls & improved prefix ops --- src/lexer.rs | 20 +++++++++----- src/main.rs | 7 +++-- src/parser.rs | 44 ++++++++++++++++++++++++------- src/parser/util.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++ src/runtime.rs | 18 ------------- 5 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 src/parser/util.rs delete mode 100644 src/runtime.rs diff --git a/src/lexer.rs b/src/lexer.rs index 3ba4ecd..b044c02 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -66,13 +66,15 @@ pub enum Token { #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum Precedence { Min, + Return, Assign, + Logical, + Equality, + Relational, AddSub, MulDiv, Pow, - Logical, - Relational, - Equality, + NegateNot, } #[derive(PartialEq, Eq)] pub enum Associativity { @@ -80,8 +82,14 @@ pub enum Associativity { Right, } impl Token { - // binop precedence ^_^ - pub fn precedence(&self) -> Option<(Precedence, Associativity)> { + pub fn prefix_precedence(&self) -> Option { + Some(match self { + Token::Return => Precedence::Return, + Token::Minus | Token::Not => Precedence::NegateNot, + _ => return None, + }) + } + pub fn infix_precedence(&self) -> Option<(Precedence, Associativity)> { Some(match self { Token::EqualTo | Token::NotEqualTo => (Precedence::Equality, Associativity::Left), Token::LessThan @@ -234,7 +242,7 @@ where '{' => self.eat_to(Token::CurlyOpen), '}' => self.eat_to(Token::CurlyClose), - // ( and ) start/end of parens (idk) + // ( and ) start/end of groups '(' => self.eat_to(Token::ParenOpen), ')' => self.eat_to(Token::ParenClose), diff --git a/src/main.rs b/src/main.rs index 4e517df..808d70e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ +use std::time::Instant; + use crate::{lexer::Lexer, parser::Parser}; mod lexer; mod parser; -mod runtime; fn main() { let script = std::fs::read_to_string("./start.leaf").unwrap(); let lexer = Lexer::new(script.chars()); let mut parser = Parser::new(lexer.map(Result::unwrap)); + let start = Instant::now(); let block = parser.parse().unwrap(); - println!("{block:?}"); + println!("Parse took {:?}", start.elapsed()); + parser::util::display(parser::Expr::Block(block)); } diff --git a/src/parser.rs b/src/parser.rs index 7d88ad7..7bddd2f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,16 +2,18 @@ use std::iter::Peekable; use crate::lexer::{Associativity, LexError, Literal, Precedence, Token}; +pub mod util; + #[derive(Debug)] pub enum Expr { // Data and variables Assignment(Box, Box), Literal(Literal), - // Control flow - Call(Box, Vec), - Return(Box), // Runtime datatypes Block(Block), + // Control flow + Return(Box), + Call(Box, Vec), // Unary operations Negate(Box), Not(Box), @@ -35,7 +37,7 @@ pub enum Expr { #[derive(Debug, Default)] pub struct Block { - exprs: Vec, + pub exprs: Vec, } #[derive(Debug)] @@ -96,6 +98,17 @@ where self.eat(); Box::new(Expr::Block(b)) } + // unary ops!! (prefix) + t if t.prefix_precedence().is_some() => { + let prec = t.prefix_precedence().unwrap(); + let rhs = self.parse_expr(prec, in_group)?; + Box::new(match t { + Token::Minus => Expr::Negate(rhs), + Token::Not => Expr::Not(rhs), + Token::Return => Expr::Return(rhs), + _ => unreachable!(), + }) + } // return Token::Return => Box::new(Expr::Return(self.parse_expr(Precedence::Min, false)?)), // not @@ -110,15 +123,28 @@ where Ok(Token::ParenClose) if in_group => break, // end (stream) Err(_) if !in_group => break, + // function call + Ok(Token::ParenOpen) => { + // eat opening paren + self.eat(); + let mut exprs = Vec::new(); + while !matches!(self.try_peek()?, Token::ParenClose) { + exprs.push(*self.parse_expr(Precedence::Min, false)?); + } + // eat closing paren + self.eat(); + lhs = Box::new(Expr::Call(lhs, exprs)); + continue; + } // operator - Ok(t) if t.precedence().is_some() => t, + Ok(t) if t.infix_precedence().is_some() => t, // unexpected token (stop trying to parse) Ok(_) => break, // unexpected end Err(err) => return Err(err), }; - let (prec, assoc) = op.precedence().unwrap(); + let (prec, assoc) = op.infix_precedence().unwrap(); // break if this op is meant for previous recursion // or it's equal and we would prefer to build leftward.. @@ -138,9 +164,9 @@ where Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs), // relational Token::LessThan => Expr::LessThan(lhs, rhs), - Token::LessThanOrEqualTo => Expr::LessThan(lhs, rhs), - Token::GreaterThan => Expr::LessThan(lhs, rhs), - Token::GreaterThanOrEqualTo => Expr::LessThan(lhs, rhs), + Token::LessThanOrEqualTo => Expr::LessThanOrEqualTo(lhs, rhs), + Token::GreaterThan => Expr::GreaterThan(lhs, rhs), + Token::GreaterThanOrEqualTo => Expr::GreaterThanOrEqualTo(lhs, rhs), // logical Token::And => Expr::And(lhs, rhs), Token::Or => Expr::Or(lhs, rhs), diff --git a/src/parser/util.rs b/src/parser/util.rs new file mode 100644 index 0000000..25dd7a5 --- /dev/null +++ b/src/parser/util.rs @@ -0,0 +1,66 @@ +use crate::parser::Expr; + +pub fn display(e: Expr) { + let result = fmt_expr(e, 0); + println!("{result}"); +} + +fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String { + format!( + "({} {} {})", + fmt_expr(left, depth), + op, + fmt_expr(right, depth) + ) +} + +fn fmt_expr(e: Expr, depth: usize) -> String { + match e { + Expr::Assignment(l, r) => fmt_binop(*l, *r, "=", depth), + Expr::Literal(l) => l.to_string(), + Expr::Call(l, r) => { + let mut result = fmt_expr(*l, depth); + result.push('('); + let len = r.len(); + for (i, e) in r.into_iter().enumerate() { + result.push_str(&fmt_expr(e, depth)); + if i + 1 != len { + result.push_str(", "); + } + } + result.push(')'); + result + } + Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)), + Expr::Block(b) => { + let mut result = String::new(); + let len = b.exprs.len(); + for (i, expr) in b.exprs.into_iter().enumerate() { + result.push_str(&" ".repeat(depth)); + result.push_str(&fmt_expr(expr, depth + 1)); + if depth != 0 || i + 1 != len { + result.push('\n'); + } + } + if depth != 0 { + result = format!("{{\n{result}{}}}", " ".repeat(depth - 1)); + } + result + } + Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)), + Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)), + Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth), + Expr::NotEqualTo(l, r) => fmt_binop(*l, *r, "!=", depth), + Expr::And(l, r) => fmt_binop(*l, *r, "&&", depth), + Expr::Or(l, r) => fmt_binop(*l, *r, "||", depth), + Expr::LessThan(l, r) => fmt_binop(*l, *r, "<", depth), + Expr::LessThanOrEqualTo(l, r) => fmt_binop(*l, *r, "<=", depth), + Expr::GreaterThan(l, r) => fmt_binop(*l, *r, ">", depth), + Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(*l, *r, ">=", depth), + Expr::Add(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::Divide(l, r) => fmt_binop(*l, *r, "/", depth), + Expr::Exponent(l, r) => fmt_binop(*l, *r, "^", depth), + } +} diff --git a/src/runtime.rs b/src/runtime.rs deleted file mode 100644 index aea3bd7..0000000 --- a/src/runtime.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::parser::{Block, Expr}; - -pub enum Value { - String(String), - Integer(i64), - Boolean(bool), - Nil, - Block(Block), -} - -fn eval(e: Expr) -> Value { - todo!() -} - -/// Evaluates all expressions of a block. -pub fn exec(b: Block) -> Value { - todo!() -}