forked from natto1784/tricc
		
	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:
		@@ -12,7 +12,7 @@ pub enum Entity {
 | 
			
		||||
    Fn(Fn),
 | 
			
		||||
    Class(Class),
 | 
			
		||||
    Module(Module),
 | 
			
		||||
    Static(Let)
 | 
			
		||||
    Static(Let),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A module just provides an additional scope
 | 
			
		||||
@@ -106,7 +106,7 @@ pub enum ElseType {
 | 
			
		||||
    Else(Vec<Statement>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Op = crate::lexer::TokenSymbol;
 | 
			
		||||
pub(crate) type Op = crate::lexer::TokenSymbol;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
pub enum Literal {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ use std::str;
 | 
			
		||||
/// All token literals
 | 
			
		||||
///
 | 
			
		||||
/// TODO: Add string
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
#[derive(Debug, PartialEq, Clone, Copy)]
 | 
			
		||||
pub enum TokenLiteral {
 | 
			
		||||
    Int,
 | 
			
		||||
    Float,
 | 
			
		||||
@@ -65,7 +65,7 @@ pub enum TokenSymbol {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// All token keywod
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
#[derive(Debug, PartialEq, Clone, Copy)]
 | 
			
		||||
pub enum TokenKeyword {
 | 
			
		||||
    // parents
 | 
			
		||||
    Fn,
 | 
			
		||||
@@ -97,7 +97,7 @@ pub enum TokenKeyword {
 | 
			
		||||
/// All token delimiters
 | 
			
		||||
///
 | 
			
		||||
/// TODO: Maybe add \[ and \]
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
#[derive(Debug, PartialEq, Clone, Copy)]
 | 
			
		||||
pub enum TokenDelimiter {
 | 
			
		||||
    BraceOpen,
 | 
			
		||||
    BraceClose,
 | 
			
		||||
@@ -106,7 +106,7 @@ pub enum TokenDelimiter {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// All tokens
 | 
			
		||||
#[derive(Debug, PartialEq)]
 | 
			
		||||
#[derive(Debug, PartialEq, Clone, Copy)]
 | 
			
		||||
pub enum TokenKind {
 | 
			
		||||
    Newline,
 | 
			
		||||
    Eof,
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,10 @@ impl<'a> Parser<'a> {
 | 
			
		||||
                        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)) {
 | 
			
		||||
                self.error_expected_peek("}");
 | 
			
		||||
                return None;
 | 
			
		||||
@@ -93,6 +97,10 @@ impl<'a> Parser<'a> {
 | 
			
		||||
                        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)) {
 | 
			
		||||
                self.error_expected_peek("}");
 | 
			
		||||
                return None;
 | 
			
		||||
@@ -148,7 +156,11 @@ impl<'a> Parser<'a> {
 | 
			
		||||
            if self.skip_token(TokenKind::Delimiter(TokenDelimiter::BraceClose)) {
 | 
			
		||||
                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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,10 @@ impl<'a> Parser<'a> {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            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)
 | 
			
		||||
@@ -325,32 +329,107 @@ impl<'a> Parser<'a> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// <https://en.wikipedia.org/wiki/Recursive_descent_parser>
 | 
			
		||||
    /// expr ::= exprControl
 | 
			
		||||
    fn parse_expr(&mut self) -> Option<Expr> {
 | 
			
		||||
    pub(super) fn parse_expr(&mut self) -> Option<Expr> {
 | 
			
		||||
        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]
 | 
			
		||||
fn test_parse_expr_literals() {
 | 
			
		||||
fn test_parse_expr() {
 | 
			
		||||
    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!(
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -108,3 +108,14 @@ impl<'a> Parser<'a> {
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,9 @@ impl<'a> Parser<'a> {
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn next_token(&mut self) -> Token {
 | 
			
		||||
        self.lexer.next_token()
 | 
			
		||||
        let t = self.lexer.next_token();
 | 
			
		||||
        println!("{:?}", t);
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
@@ -61,7 +63,7 @@ impl<'a> Parser<'a> {
 | 
			
		||||
        return self.lexer.peek_token();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// newline ::= "\n"
 | 
			
		||||
    /// newline ::= "}\n"
 | 
			
		||||
    fn trim_newlines(&mut self) {
 | 
			
		||||
        while self.peek_token().kind == TokenKind::Newline {
 | 
			
		||||
            self.next_token();
 | 
			
		||||
@@ -77,6 +79,14 @@ impl<'a> Parser<'a> {
 | 
			
		||||
        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"
 | 
			
		||||
    fn parse_ty(&mut self) -> Option<Ty> {
 | 
			
		||||
        let ty: Ty;
 | 
			
		||||
@@ -134,6 +144,10 @@ impl<'a> Parser<'a> {
 | 
			
		||||
                TokenKind::Eof => break,
 | 
			
		||||
                _ => {
 | 
			
		||||
                    parent.push(self.parse_entity()?);
 | 
			
		||||
                    if !self.check_newline_or_tok(TokenKind::Eof) {
 | 
			
		||||
                        self.error_expected_peek("newline or end of file");
 | 
			
		||||
                        return None;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,12 @@ impl<'a> Parser<'a> {
 | 
			
		||||
    /// statement ::= static | let | expr
 | 
			
		||||
    pub(super) fn parse_statement(&mut self) -> Option<Statement> {
 | 
			
		||||
        use TokenKeyword::*;
 | 
			
		||||
        println!("STMT");
 | 
			
		||||
 | 
			
		||||
        Some(match self.peek_token().kind {
 | 
			
		||||
            TokenKind::Keyword(Static) => Statement::Static(self.parse_static()?),
 | 
			
		||||
            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 expr = if self.skip_token(TokenKind::Symbol(TokenSymbol::Eq)) {
 | 
			
		||||
            self.parse_expr_ln()
 | 
			
		||||
        } else if self.skip_token(TokenKind::Newline) {
 | 
			
		||||
            self.parse_expr()
 | 
			
		||||
        } else if self.peek_token().kind == TokenKind::Newline {
 | 
			
		||||
            None
 | 
			
		||||
        } else {
 | 
			
		||||
            self.error_expected_peek("= or newline");
 | 
			
		||||
@@ -67,6 +68,7 @@ fn test_parse_let() {
 | 
			
		||||
            expr: Some(Expr::Literal(Int(4)))
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(parser.skip_token(TokenKind::Newline), true);
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        parser.parse_let(),
 | 
			
		||||
        Some(Let {
 | 
			
		||||
@@ -75,6 +77,7 @@ fn test_parse_let() {
 | 
			
		||||
            expr: Some(Expr::Literal(Char('6')))
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(parser.skip_token(TokenKind::Newline), true);
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        parser.parse_static(),
 | 
			
		||||
        Some(Let {
 | 
			
		||||
@@ -83,5 +86,6 @@ fn test_parse_let() {
 | 
			
		||||
            expr: None
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(parser.skip_token(TokenKind::Newline), true);
 | 
			
		||||
    assert_eq!(parser.parse_let(), None);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user