rue-parser 0.8.4

A parser for the Rue programming language.
Documentation
use rowan::Checkpoint;

use crate::{
    Parser, SyntaxKind, T,
    grammar::{
        binding::binding,
        expr::{ExprOptions, expr, expr_with},
        ty::ty,
    },
};

#[must_use]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatementKind {
    Normal,
    End,
}

pub fn stmt(p: &mut Parser) -> StatementKind {
    let cp = p.checkpoint();

    let inline = p.try_eat(T![inline]);

    if p.at(T![let]) {
        let_stmt(p, cp);
    } else if p.at(T![return]) && !inline {
        return_stmt(p);
    } else if p.at(T![assert]) && !inline {
        assert_stmt(p);
    } else if p.at(T![raise]) && !inline {
        raise_stmt(p);
    } else if p.at(T![debug]) && !inline {
        debug_stmt(p);
    } else {
        if expr_with(
            p,
            cp,
            ExprOptions {
                minimum_binding_power: 0,
                allow_statement: true,
                allow_struct_initializer: true,
                inline,
            },
        ) {
            return StatementKind::Normal;
        }
        if p.at(T![;]) {
            p.start_at(cp, SyntaxKind::ExprStmt);
            p.expect(T![;]);
            p.finish();
        } else {
            return StatementKind::End;
        }
    }
    StatementKind::Normal
}

fn let_stmt(p: &mut Parser, cp: Checkpoint) {
    p.start_at(cp, SyntaxKind::LetStmt);
    p.try_eat(T![inline]);
    p.expect(T![let]);
    binding(p);
    if p.try_eat(T![:]) {
        ty(p);
    }
    if p.try_eat(T![=]) {
        expr(p);
    }
    p.expect(T![;]);
    p.finish();
}

fn return_stmt(p: &mut Parser) {
    p.start(SyntaxKind::ReturnStmt);
    p.expect(T![return]);
    if !p.at(T![;]) {
        expr(p);
    }
    p.expect(T![;]);
    p.finish();
}

fn assert_stmt(p: &mut Parser) {
    p.start(SyntaxKind::AssertStmt);
    p.expect(T![assert]);
    expr(p);
    p.expect(T![;]);
    p.finish();
}

fn raise_stmt(p: &mut Parser) {
    p.start(SyntaxKind::RaiseStmt);
    p.expect(T![raise]);
    if !p.at(T![;]) {
        expr(p);
    }
    p.expect(T![;]);
    p.finish();
}

fn debug_stmt(p: &mut Parser) {
    p.start(SyntaxKind::DebugStmt);
    p.expect(T![debug]);
    expr(p);
    p.expect(T![;]);
    p.finish();
}

#[cfg(test)]
mod tests {
    use expect_test::{Expect, expect};

    use crate::grammar::tests::check;

    use super::*;

    fn check_stmt(kind: StatementKind, source: &str, expect: Expect, errors: Expect) {
        check(
            |p| {
                assert_eq!(stmt(p), kind);
            },
            source,
            expect,
            errors,
        );
    }

    #[test]
    fn test_let_stmt() {
        check_stmt(
            StatementKind::Normal,
            "let x = 5;",
            expect![[r#"
                LetStmt@0..10
                  Let@0..3 "let"
                  Whitespace@3..4 " "
                  NamedBinding@4..5
                    Ident@4..5 "x"
                  Whitespace@5..6 " "
                  Assign@6..7 "="
                  Whitespace@7..8 " "
                  LiteralExpr@8..9
                    Integer@8..9 "5"
                  Semicolon@9..10 ";"
            "#]],
            expect![],
        );

        check_stmt(
            StatementKind::Normal,
            "let thing: Int = 42 + 3;",
            expect![[r#"
                LetStmt@0..24
                  Let@0..3 "let"
                  Whitespace@3..4 " "
                  NamedBinding@4..9
                    Ident@4..9 "thing"
                  Colon@9..10 ":"
                  Whitespace@10..11 " "
                  PathType@11..15
                    PathSegment@11..15
                      Ident@11..14 "Int"
                      Whitespace@14..15 " "
                  Assign@15..16 "="
                  Whitespace@16..17 " "
                  BinaryExpr@17..23
                    LiteralExpr@17..19
                      Integer@17..19 "42"
                    Whitespace@19..20 " "
                    Plus@20..21 "+"
                    Whitespace@21..22 " "
                    LiteralExpr@22..23
                      Integer@22..23 "3"
                  Semicolon@23..24 ";"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_expr_stmt_without_semicolon() {
        check_stmt(
            StatementKind::End,
            "(42 + 3)",
            expect![[r#"
                GroupExpr@0..8
                  OpenParen@0..1 "("
                  BinaryExpr@1..7
                    LiteralExpr@1..3
                      Integer@1..3 "42"
                    Whitespace@3..4 " "
                    Plus@4..5 "+"
                    Whitespace@5..6 " "
                    LiteralExpr@6..7
                      Integer@6..7 "3"
                  CloseParen@7..8 ")"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_expr_stmt_with_semicolon() {
        check_stmt(
            StatementKind::Normal,
            "(42 + 3);",
            expect![[r#"
                ExprStmt@0..9
                  GroupExpr@0..8
                    OpenParen@0..1 "("
                    BinaryExpr@1..7
                      LiteralExpr@1..3
                        Integer@1..3 "42"
                      Whitespace@3..4 " "
                      Plus@4..5 "+"
                      Whitespace@5..6 " "
                      LiteralExpr@6..7
                        Integer@6..7 "3"
                    CloseParen@7..8 ")"
                  Semicolon@8..9 ";"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_if_stmt() {
        check_stmt(
            StatementKind::Normal,
            "if true { 42 }",
            expect![[r#"
                IfStmt@0..14
                  If@0..2 "if"
                  Whitespace@2..3 " "
                  LiteralExpr@3..7
                    True@3..7 "true"
                  Whitespace@7..8 " "
                  Block@8..14
                    OpenBrace@8..9 "{"
                    Whitespace@9..10 " "
                    LiteralExpr@10..12
                      Integer@10..12 "42"
                    Whitespace@12..13 " "
                    CloseBrace@13..14 "}"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_return_stmt() {
        check_stmt(
            StatementKind::Normal,
            "return 42;",
            expect![[r#"
                ReturnStmt@0..10
                  Return@0..6 "return"
                  Whitespace@6..7 " "
                  LiteralExpr@7..9
                    Integer@7..9 "42"
                  Semicolon@9..10 ";"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_assert_stmt() {
        check_stmt(
            StatementKind::Normal,
            "return 42;",
            expect![[r#"
                ReturnStmt@0..10
                  Return@0..6 "return"
                  Whitespace@6..7 " "
                  LiteralExpr@7..9
                    Integer@7..9 "42"
                  Semicolon@9..10 ";"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_raise_stmt() {
        check_stmt(
            StatementKind::Normal,
            "return 42;",
            expect![[r#"
                ReturnStmt@0..10
                  Return@0..6 "return"
                  Whitespace@6..7 " "
                  LiteralExpr@7..9
                    Integer@7..9 "42"
                  Semicolon@9..10 ";"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_debug_stmt() {
        check_stmt(
            StatementKind::Normal,
            "debug 42;",
            expect![[r#"
                DebugStmt@0..9
                  Debug@0..5 "debug"
                  Whitespace@5..6 " "
                  LiteralExpr@6..8
                    Integer@6..8 "42"
                  Semicolon@8..9 ";"
            "#]],
            expect![],
        );
    }
}