parser: fix newline checks after statements

since there is no EOL delimiter like ';', we have to check for newline to avoid something like `a = 4 + 4 b = a`
also added tests for expr parsing

Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
2023-08-14 00:56:09 +05:30
parent 360687f3c0
commit af30410aab
7 changed files with 150 additions and 30 deletions

View File

@@ -12,7 +12,7 @@ pub enum Entity {
Fn(Fn), Fn(Fn),
Class(Class), Class(Class),
Module(Module), Module(Module),
Static(Let) Static(Let),
} }
/// A module just provides an additional scope /// A module just provides an additional scope
@@ -106,7 +106,7 @@ pub enum ElseType {
Else(Vec<Statement>), Else(Vec<Statement>),
} }
type Op = crate::lexer::TokenSymbol; pub(crate) type Op = crate::lexer::TokenSymbol;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Literal { pub enum Literal {

View File

@@ -6,7 +6,7 @@ use std::str;
/// All token literals /// All token literals
/// ///
/// TODO: Add string /// TODO: Add string
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenLiteral { pub enum TokenLiteral {
Int, Int,
Float, Float,
@@ -65,7 +65,7 @@ pub enum TokenSymbol {
} }
/// All token keywod /// All token keywod
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenKeyword { pub enum TokenKeyword {
// parents // parents
Fn, Fn,
@@ -97,7 +97,7 @@ pub enum TokenKeyword {
/// All token delimiters /// All token delimiters
/// ///
/// TODO: Maybe add \[ and \] /// TODO: Maybe add \[ and \]
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenDelimiter { pub enum TokenDelimiter {
BraceOpen, BraceOpen,
BraceClose, BraceClose,
@@ -106,7 +106,7 @@ pub enum TokenDelimiter {
} }
/// All tokens /// All tokens
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenKind { pub enum TokenKind {
Newline, Newline,
Eof, Eof,

View File

@@ -57,6 +57,10 @@ impl<'a> Parser<'a> {
return None; return None;
} }
}); });
if !self.check_newline_or_tok(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("newline or }");
return None;
}
} else if !self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) { } else if !self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("}"); self.error_expected_peek("}");
return None; return None;
@@ -93,6 +97,10 @@ impl<'a> Parser<'a> {
return None; return None;
} }
}); });
if !self.check_newline_or_tok(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("newline or }");
return None;
}
} else if !self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) { } else if !self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("}"); self.error_expected_peek("}");
return None; return None;
@@ -148,7 +156,11 @@ impl<'a> Parser<'a> {
if self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) { if self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
break; break;
} }
children.push(self.parse_statement()?) children.push(self.parse_statement()?);
if !self.check_newline_or_tok(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("newline or }");
return None;
}
} }
Some(Fn { Some(Fn {

View File

@@ -57,6 +57,10 @@ impl<'a> Parser<'a> {
break; break;
} }
statements.push(self.parse_statement()?); statements.push(self.parse_statement()?);
if !self.check_newline_or_tok(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
self.error_expected_peek("newline or }");
return None;
}
} }
Some(statements) Some(statements)
@@ -325,32 +329,107 @@ impl<'a> Parser<'a> {
/// ///
/// <https://en.wikipedia.org/wiki/Recursive_descent_parser> /// <https://en.wikipedia.org/wiki/Recursive_descent_parser>
/// expr ::= exprControl /// expr ::= exprControl
fn parse_expr(&mut self) -> Option<Expr> { pub(super) fn parse_expr(&mut self) -> Option<Expr> {
self.parse_expr_control() self.parse_expr_control()
} }
pub(super) fn parse_expr_ln(&mut self) -> Option<Expr> {
let expr = self.parse_expr();
if !self.skip_token(TokenKind::Newline) {
self.error_expected_peek("newline");
return None;
}
expr
}
} }
#[test] #[test]
fn test_parse_expr_literals() { fn test_parse_expr() {
use Literal::*; use Literal::*;
use TokenSymbol::*;
macro_rules! b {
($expr:expr) => {
Box::new($expr)
};
}
let mut parser = Parser::new(
r#"if if 1 { 1 } else { 0 } + 9 {
a = 4
} else if 1 {
a = 5
} else {
}
amul ^= (4 + 93 * (1 << 3) / 1.44) ^ bhatura
stove = { 44 } + amul"#,
);
let mut parser = Parser::new("4524 3123.15e4 9e2 9083482.429455 'c' 3331.13.3");
assert_eq!(parser.parse_expr(), Some(Expr::Literal(Int(4524))));
assert_eq!(parser.parse_expr(), Some(Expr::Literal(Float(3123.15e4))));
assert_eq!(parser.parse_expr(), Some(Expr::Literal(Float(9e2))));
assert_eq!( assert_eq!(
parser.parse_expr(), parser.parse_expr(),
Some(Expr::Literal(Float(9083482.429455))) Some(Expr::If(If {
cond: b!(Expr::Op(
Plus,
b!(Expr::If(If {
cond: b!(Expr::Literal(Int(1))),
then: vec![Statement::Expr(Expr::Literal(Int(1)))],
or: Some(b!(ElseType::Else(vec![Statement::Expr(Expr::Literal(
Int(0)
))])))
})),
Some(b!(Expr::Literal(Int(9))))
)),
then: vec![Statement::Expr(Expr::Op(
Eq,
b!(Expr::Identifier("a".into())),
Some(b!(Expr::Literal(Int(4))))
))],
or: Some(b!(ElseType::If(If {
cond: b!(Expr::Literal(Int(1))),
then: vec![Statement::Expr(Expr::Op(
Eq,
b!(Expr::Identifier("a".into())),
Some(b!(Expr::Literal(Int(5))))
))],
or: Some(b!(ElseType::Else(vec![])))
})))
}))
);
assert_eq!(parser.skip_token(TokenKind::Newline), true);
assert_eq!(
parser.parse_expr(),
Some(Expr::Op(
CaretEq,
b!(Expr::Identifier("amul".into())),
Some(b!(Expr::Op(
Caret,
b!(Expr::Op(
Plus,
b!(Expr::Literal(Int(4))),
Some(b!(Expr::Op(
Star,
b!(Expr::Literal(Int(93))),
Some(b!(Expr::Op(
Slash,
b!(Expr::Op(
Shl,
b!(Expr::Literal(Int(1))),
Some(b!(Expr::Literal(Int(3))))
)),
Some(b!(Expr::Literal(Float(1.44))))
)))
)))
)),
Some(b!(Expr::Identifier("bhatura".into())))
)))
))
);
assert_eq!(parser.skip_token(TokenKind::Newline), true);
assert_eq!(
parser.parse_expr(),
Some(Expr::Op(
Eq,
b!(Expr::Identifier("stove".into())),
Some(b!(Expr::Op(
Plus,
b!(Expr::Block(vec![Statement::Expr(Expr::Literal(Int(44)))])),
Some(b!(Expr::Identifier("amul".into())))
)))
))
); );
assert_eq!(parser.parse_expr(), Some(Expr::Literal(Char('c'))));
assert_eq!(parser.parse_expr(), None);
} }

View File

@@ -108,3 +108,14 @@ impl<'a> Parser<'a> {
self.next_token().val.chars().nth(1) self.next_token().val.chars().nth(1)
} }
} }
#[test]
fn test_parse_literals() {
let mut parser = Parser::new("4524 3123.15e4 9e2 9083482.429455 'c' 3331.13.1");
assert_eq!(parser.parse_int(), Some(4524));
assert_eq!(parser.parse_float(), Some(3123.15e4));
assert_eq!(parser.parse_float(), Some(9e2));
assert_eq!(parser.parse_float(), Some(9083482.429455));
assert_eq!(parser.parse_char(), Some('c'));
assert_eq!(parser.next_token().kind, TokenKind::Invalid);
}

View File

@@ -53,7 +53,9 @@ impl<'a> Parser<'a> {
#[inline] #[inline]
fn next_token(&mut self) -> Token { fn next_token(&mut self) -> Token {
self.lexer.next_token() let t = self.lexer.next_token();
println!("{:?}", t);
t
} }
#[inline] #[inline]
@@ -61,7 +63,7 @@ impl<'a> Parser<'a> {
return self.lexer.peek_token(); return self.lexer.peek_token();
} }
/// newline ::= "\n" /// newline ::= "}\n"
fn trim_newlines(&mut self) { fn trim_newlines(&mut self) {
while self.peek_token().kind == TokenKind::Newline { while self.peek_token().kind == TokenKind::Newline {
self.next_token(); self.next_token();
@@ -77,6 +79,14 @@ impl<'a> Parser<'a> {
false false
} }
fn check_newline_or_tok(&mut self, token: TokenKind) -> bool {
match self.peek_token().kind {
TokenKind::Newline => true,
d if d == token => true,
_ => false,
}
}
/// ty ::= "int" | "float" | "char" /// ty ::= "int" | "float" | "char"
fn parse_ty(&mut self) -> Option<Ty> { fn parse_ty(&mut self) -> Option<Ty> {
let ty: Ty; let ty: Ty;
@@ -134,6 +144,10 @@ impl<'a> Parser<'a> {
TokenKind::Eof => break, TokenKind::Eof => break,
_ => { _ => {
parent.push(self.parse_entity()?); parent.push(self.parse_entity()?);
if !self.check_newline_or_tok(TokenKind::Eof) {
self.error_expected_peek("newline or end of file");
return None;
}
} }
} }
} }

View File

@@ -10,11 +10,12 @@ impl<'a> Parser<'a> {
/// statement ::= static | let | expr /// statement ::= static | let | expr
pub(super) fn parse_statement(&mut self) -> Option<Statement> { pub(super) fn parse_statement(&mut self) -> Option<Statement> {
use TokenKeyword::*; use TokenKeyword::*;
println!("STMT");
Some(match self.peek_token().kind { Some(match self.peek_token().kind {
TokenKind::Keyword(Static) => Statement::Static(self.parse_static()?), TokenKind::Keyword(Static) => Statement::Static(self.parse_static()?),
TokenKind::Keyword(Let) => Statement::Let(self.parse_let()?), TokenKind::Keyword(Let) => Statement::Let(self.parse_let()?),
_ => Statement::Expr(self.parse_expr_ln()?), _ => Statement::Expr(self.parse_expr()?),
}) })
} }
@@ -37,8 +38,8 @@ impl<'a> Parser<'a> {
let (name, ty) = self.parse_ident_with_ty()?; let (name, ty) = self.parse_ident_with_ty()?;
let expr = if self.skip_token(TokenKind::Symbol(TokenSymbol::Eq)) { let expr = if self.skip_token(TokenKind::Symbol(TokenSymbol::Eq)) {
self.parse_expr_ln() self.parse_expr()
} else if self.skip_token(TokenKind::Newline) { } else if self.peek_token().kind == TokenKind::Newline {
None None
} else { } else {
self.error_expected_peek("= or newline"); self.error_expected_peek("= or newline");
@@ -67,6 +68,7 @@ fn test_parse_let() {
expr: Some(Expr::Literal(Int(4))) expr: Some(Expr::Literal(Int(4)))
}) })
); );
assert_eq!(parser.skip_token(TokenKind::Newline), true);
assert_eq!( assert_eq!(
parser.parse_let(), parser.parse_let(),
Some(Let { Some(Let {
@@ -75,6 +77,7 @@ fn test_parse_let() {
expr: Some(Expr::Literal(Char('6'))) expr: Some(Expr::Literal(Char('6')))
}) })
); );
assert_eq!(parser.skip_token(TokenKind::Newline), true);
assert_eq!( assert_eq!(
parser.parse_static(), parser.parse_static(),
Some(Let { Some(Let {
@@ -83,5 +86,6 @@ fn test_parse_let() {
expr: None expr: None
}) })
); );
assert_eq!(parser.skip_token(TokenKind::Newline), true);
assert_eq!(parser.parse_let(), None); assert_eq!(parser.parse_let(), None);
} }