From 77246ddd246653d2d5fa16f075ce55778ae264bf Mon Sep 17 00:00:00 2001 From: minish Date: Fri, 11 Jul 2025 22:41:28 -0400 Subject: [PATCH] func args + some fixes --- src/lexer.rs | 18 ++++---- src/parser.rs | 101 ++++++++++++++++++++++++++++++++------------- src/parser/util.rs | 10 ++++- 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index 5b3eb26..b48e1b1 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -36,6 +36,7 @@ pub enum Token { Minus, Star, Slash, + Percent, Caret, CurlyOpen, @@ -47,6 +48,7 @@ pub enum Token { Comma, Eol, + Func, If, Else, Return, @@ -69,8 +71,6 @@ pub enum Token { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum Precedence { Min, - Return, - If, Assign, Logical, Equality, @@ -78,7 +78,7 @@ pub enum Precedence { AddSub, MulDiv, Pow, - NegateNot, + Prefix, } #[derive(PartialEq, Eq)] pub enum Associativity { @@ -88,9 +88,9 @@ pub enum Associativity { impl Token { pub fn prefix_precedence(&self) -> Option { Some(match self { - Token::Return => Precedence::Return, - Token::If => Precedence::If, - Token::Minus | Token::Not => Precedence::NegateNot, + Token::Return | Token::If | Token::Func | Token::Minus | Token::Not => { + Precedence::Prefix + } _ => return None, }) } @@ -195,9 +195,10 @@ where } match word.as_str() { - "return" => Token::Return, + "func" => Token::Func, "if" => Token::If, "else" => Token::Else, + "return" => Token::Return, "true" => Token::Literal(Literal::Boolean(true)), "false" => Token::Literal(Literal::Boolean(false)), "nil" => Token::Literal(Literal::Nil), @@ -270,6 +271,9 @@ where // / divide '/' => self.eat_to(Token::Slash), + // % modulo + '%' => self.eat_to(Token::Percent), + // ^ pow '^' => self.eat_to(Token::Caret), diff --git a/src/parser.rs b/src/parser.rs index bce2ed8..6634218 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -9,8 +9,9 @@ pub enum Expr { // Data and variables Assignment(Box, Box), Literal(Literal), - // Runtime datatypes + // Non-literal datatypes Block(Block), + Func(Vec, Box), // Control flow If(Box, Box, Option>), Return(Box), @@ -66,7 +67,7 @@ pub type Result = std::result::Result; pub struct Parser> { tokens: Peekable, - is_next_eol: bool, + saw_eol: bool, } impl Parser where @@ -76,7 +77,7 @@ where let tokens = tokens.peekable(); Self { tokens, - is_next_eol: false, + saw_eol: false, } } @@ -100,12 +101,12 @@ where // Peek doesn't advance the token stream, so // don't allow it to unset the EOL flag if self.skip_eol() { - self.is_next_eol = true; + self.saw_eol = true; } self.tokens.peek().ok_or(ParseError::UnexpectedEnd) } fn try_next(&mut self) -> Result { - self.is_next_eol = self.skip_eol(); + self.saw_eol = self.skip_eol(); self.tokens.next().ok_or(ParseError::UnexpectedEnd) } @@ -133,26 +134,46 @@ where // 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), - Token::If => { - // parse the true case - let true_case = self.parse_expr(Precedence::Min, false)?; - // and maybe a false case - let false_case = matches!(self.try_peek(), Ok(Token::Else)) - .then(|| { - self.eat(); - self.parse_expr(Precedence::Min, false) - }) - .transpose()?; - // pack - Expr::If(rhs, true_case, false_case) + // parse function + if matches!(t, Token::Func) { + // expect opening paren + let next = self.try_next()?; + if !matches!(next, Token::ParenOpen) { + return Err(ParseError::UnexpectedToken(next)); } - _ => unreachable!(), - }) + // parse args + let args = self.parse_args()?; + // expect closing paren + if !matches!(self.try_peek(), Ok(Token::ParenClose)) { + return Err(ParseError::UnexpectedToken(self.next_unwrap())); + } + self.eat(); + // parse body + let body = self.parse_expr(prec, in_group)?; + // pack + Box::new(Expr::Func(args, body)) + } else { + 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), + Token::If => { + // parse the true case + let true_case = self.parse_expr(prec, in_group)?; + // and maybe a false case + let false_case = matches!(self.try_peek(), Ok(Token::Else)) + .then(|| { + self.eat(); + self.parse_expr(prec, in_group) + }) + .transpose()?; + // pack + Expr::If(rhs, true_case, false_case) + } + _ => unreachable!(), + }) + } } // unexpected token @@ -173,7 +194,7 @@ where // function call Ok(Token::ParenOpen) => { - if self.is_next_eol { + if self.saw_eol { break; } @@ -206,7 +227,7 @@ where 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.. + // or it's equal and we prefer to build leftward if prec < min_prec || (prec == min_prec && assoc == Associativity::Left) { break; } @@ -246,6 +267,24 @@ where Ok(lhs) } + fn parse_args(&mut self) -> Result> { + let mut exprs = Vec::new(); + while !matches!(self.try_peek(), Ok(Token::ParenClose)) { + // try to parse expr + exprs.push(*self.parse_expr(Precedence::Min, false)?); + // advance + let next = self.try_next()?; + // check if its the end + if matches!(next, Token::ParenClose) { + break; + } + // expect comma + if !matches!(next, Token::Comma) { + return Err(ParseError::UnexpectedToken(next)); + } + } + Ok(exprs) + } fn parse_block(&mut self, in_block: bool) -> Result { let mut exprs = Vec::new(); loop { @@ -256,10 +295,16 @@ where Err(ParseError::UnexpectedEnd) if !in_block => break, // try to parse expr - Ok(_) => exprs.push(*self.parse_expr(Precedence::Min, false)?), + Ok(_) => { + exprs.push(*self.parse_expr(Precedence::Min, false)?); + // expect eol or eof + if !matches!(self.try_peek(), Err(ParseError::UnexpectedEnd)) && !self.saw_eol { + return Err(ParseError::UnexpectedToken(self.next_unwrap())); + } + } // invalid - Err(_) => unreachable!(), + Err(err) => return Err(err), } } Ok(Block { exprs }) diff --git a/src/parser/util.rs b/src/parser/util.rs index 4b5ec9c..7ca63ca 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -38,7 +38,7 @@ fn fmt_expr(e: Expr, depth: usize) -> String { } result } - Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)), + Expr::Return(l) => format!("return ({})", fmt_expr(*l, depth)), Expr::Block(b) => { let mut result = String::new(); let len = b.exprs.len(); @@ -54,6 +54,14 @@ fn fmt_expr(e: Expr, depth: usize) -> String { } result } + Expr::Func(a, e) => format!( + "(func({}) ({}))", + a.into_iter() + .map(|e| fmt_expr(e, depth)) + .collect::>() + .join(", "), + fmt_expr(*e, depth) + ), 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),