func args + some fixes

This commit is contained in:
minish 2025-07-11 22:41:28 -04:00
parent 21616e2723
commit 77246ddd24
Signed by: min
SSH Key Fingerprint: SHA256:h4k7JNrfe1dzv1WE3oGVeAY9DPSZXIu3/j89+6DtHWE
3 changed files with 93 additions and 36 deletions

View File

@ -36,6 +36,7 @@ pub enum Token {
Minus, Minus,
Star, Star,
Slash, Slash,
Percent,
Caret, Caret,
CurlyOpen, CurlyOpen,
@ -47,6 +48,7 @@ pub enum Token {
Comma, Comma,
Eol, Eol,
Func,
If, If,
Else, Else,
Return, Return,
@ -69,8 +71,6 @@ pub enum Token {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Precedence { pub enum Precedence {
Min, Min,
Return,
If,
Assign, Assign,
Logical, Logical,
Equality, Equality,
@ -78,7 +78,7 @@ pub enum Precedence {
AddSub, AddSub,
MulDiv, MulDiv,
Pow, Pow,
NegateNot, Prefix,
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum Associativity { pub enum Associativity {
@ -88,9 +88,9 @@ pub enum Associativity {
impl Token { impl Token {
pub fn prefix_precedence(&self) -> Option<Precedence> { pub fn prefix_precedence(&self) -> Option<Precedence> {
Some(match self { Some(match self {
Token::Return => Precedence::Return, Token::Return | Token::If | Token::Func | Token::Minus | Token::Not => {
Token::If => Precedence::If, Precedence::Prefix
Token::Minus | Token::Not => Precedence::NegateNot, }
_ => return None, _ => return None,
}) })
} }
@ -195,9 +195,10 @@ where
} }
match word.as_str() { match word.as_str() {
"return" => Token::Return, "func" => Token::Func,
"if" => Token::If, "if" => Token::If,
"else" => Token::Else, "else" => Token::Else,
"return" => Token::Return,
"true" => Token::Literal(Literal::Boolean(true)), "true" => Token::Literal(Literal::Boolean(true)),
"false" => Token::Literal(Literal::Boolean(false)), "false" => Token::Literal(Literal::Boolean(false)),
"nil" => Token::Literal(Literal::Nil), "nil" => Token::Literal(Literal::Nil),
@ -270,6 +271,9 @@ where
// / divide // / divide
'/' => self.eat_to(Token::Slash), '/' => self.eat_to(Token::Slash),
// % modulo
'%' => self.eat_to(Token::Percent),
// ^ pow // ^ pow
'^' => self.eat_to(Token::Caret), '^' => self.eat_to(Token::Caret),

View File

@ -9,8 +9,9 @@ pub enum Expr {
// Data and variables // Data and variables
Assignment(Box<Expr>, Box<Expr>), Assignment(Box<Expr>, Box<Expr>),
Literal(Literal), Literal(Literal),
// Runtime datatypes // Non-literal datatypes
Block(Block), Block(Block),
Func(Vec<Expr>, Box<Expr>),
// Control flow // Control flow
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>), If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
Return(Box<Expr>), Return(Box<Expr>),
@ -66,7 +67,7 @@ pub type Result<T> = std::result::Result<T, ParseError>;
pub struct Parser<I: Iterator<Item = Token>> { pub struct Parser<I: Iterator<Item = Token>> {
tokens: Peekable<I>, tokens: Peekable<I>,
is_next_eol: bool, saw_eol: bool,
} }
impl<I> Parser<I> impl<I> Parser<I>
where where
@ -76,7 +77,7 @@ where
let tokens = tokens.peekable(); let tokens = tokens.peekable();
Self { Self {
tokens, tokens,
is_next_eol: false, saw_eol: false,
} }
} }
@ -100,12 +101,12 @@ where
// Peek doesn't advance the token stream, so // Peek doesn't advance the token stream, so
// don't allow it to unset the EOL flag // don't allow it to unset the EOL flag
if self.skip_eol() { if self.skip_eol() {
self.is_next_eol = true; self.saw_eol = true;
} }
self.tokens.peek().ok_or(ParseError::UnexpectedEnd) self.tokens.peek().ok_or(ParseError::UnexpectedEnd)
} }
fn try_next(&mut self) -> Result<Token> { fn try_next(&mut self) -> Result<Token> {
self.is_next_eol = self.skip_eol(); self.saw_eol = self.skip_eol();
self.tokens.next().ok_or(ParseError::UnexpectedEnd) self.tokens.next().ok_or(ParseError::UnexpectedEnd)
} }
@ -133,26 +134,46 @@ where
// unary ops!! (prefix) // unary ops!! (prefix)
t if t.prefix_precedence().is_some() => { t if t.prefix_precedence().is_some() => {
let prec = t.prefix_precedence().unwrap(); let prec = t.prefix_precedence().unwrap();
let rhs = self.parse_expr(prec, in_group)?; // parse function
Box::new(match t { if matches!(t, Token::Func) {
Token::Minus => Expr::Negate(rhs), // expect opening paren
Token::Not => Expr::Not(rhs), let next = self.try_next()?;
Token::Return => Expr::Return(rhs), if !matches!(next, Token::ParenOpen) {
Token::If => { return Err(ParseError::UnexpectedToken(next));
// 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)
} }
_ => 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 // unexpected token
@ -173,7 +194,7 @@ where
// function call // function call
Ok(Token::ParenOpen) => { Ok(Token::ParenOpen) => {
if self.is_next_eol { if self.saw_eol {
break; break;
} }
@ -206,7 +227,7 @@ where
let (prec, assoc) = op.infix_precedence().unwrap(); let (prec, assoc) = op.infix_precedence().unwrap();
// break if this op is meant for previous recursion // 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) { if prec < min_prec || (prec == min_prec && assoc == Associativity::Left) {
break; break;
} }
@ -246,6 +267,24 @@ where
Ok(lhs) Ok(lhs)
} }
fn parse_args(&mut self) -> Result<Vec<Expr>> {
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<Block> { fn parse_block(&mut self, in_block: bool) -> Result<Block> {
let mut exprs = Vec::new(); let mut exprs = Vec::new();
loop { loop {
@ -256,10 +295,16 @@ where
Err(ParseError::UnexpectedEnd) if !in_block => break, Err(ParseError::UnexpectedEnd) if !in_block => break,
// try to parse expr // 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 // invalid
Err(_) => unreachable!(), Err(err) => return Err(err),
} }
} }
Ok(Block { exprs }) Ok(Block { exprs })

View File

@ -38,7 +38,7 @@ fn fmt_expr(e: Expr, depth: usize) -> String {
} }
result result
} }
Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)), Expr::Return(l) => format!("return ({})", fmt_expr(*l, depth)),
Expr::Block(b) => { Expr::Block(b) => {
let mut result = String::new(); let mut result = String::new();
let len = b.exprs.len(); let len = b.exprs.len();
@ -54,6 +54,14 @@ fn fmt_expr(e: Expr, depth: usize) -> String {
} }
result result
} }
Expr::Func(a, e) => format!(
"(func({}) ({}))",
a.into_iter()
.map(|e| fmt_expr(e, depth))
.collect::<Vec<_>>()
.join(", "),
fmt_expr(*e, depth)
),
Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)), Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)),
Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)), Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)),
Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth), Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth),