func args + some fixes
This commit is contained in:
parent
21616e2723
commit
77246ddd24
18
src/lexer.rs
18
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<Precedence> {
|
||||
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),
|
||||
|
||||
|
|
101
src/parser.rs
101
src/parser.rs
|
@ -9,8 +9,9 @@ pub enum Expr {
|
|||
// Data and variables
|
||||
Assignment(Box<Expr>, Box<Expr>),
|
||||
Literal(Literal),
|
||||
// Runtime datatypes
|
||||
// Non-literal datatypes
|
||||
Block(Block),
|
||||
Func(Vec<Expr>, Box<Expr>),
|
||||
// Control flow
|
||||
If(Box<Expr>, Box<Expr>, Option<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>> {
|
||||
tokens: Peekable<I>,
|
||||
is_next_eol: bool,
|
||||
saw_eol: bool,
|
||||
}
|
||||
impl<I> Parser<I>
|
||||
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<Token> {
|
||||
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<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> {
|
||||
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 })
|
||||
|
|
|
@ -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::<Vec<_>>()
|
||||
.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),
|
||||
|
|
Loading…
Reference in New Issue