rue-parser 0.8.4

A parser for the Rue programming language.
Documentation
use crate::{
    Parser, SyntaxKind, T,
    grammar::generics::{generic_arguments, generic_parameters},
};

pub fn ty(p: &mut Parser) {
    ty_inner(p, true);
}

fn ty_inner(p: &mut Parser, allow_union: bool) {
    let cp = p.checkpoint();

    if p.at(SyntaxKind::Ident) || p.at(T![super]) || p.at(T![::]) {
        path_type(p);
    } else if let Some(kind) = p.at_any(SyntaxKind::LITERAL) {
        p.start(SyntaxKind::LiteralType);
        p.expect(kind);
        p.finish();
    } else if p.at(T!['(']) {
        p.expect(T!['(']);
        ty(p);
        if p.try_eat(T![,]) {
            p.start_at(cp, SyntaxKind::PairType);
            ty(p);
            p.try_eat(T![,]);
        } else {
            p.start_at(cp, SyntaxKind::GroupType);
        }
        p.expect(T![')']);
        p.finish();
    } else if p.at(T!['[']) {
        p.start_at(cp, SyntaxKind::ListType);
        p.expect(T!['[']);
        while !p.at(T![']']) {
            p.start(SyntaxKind::ListTypeItem);
            p.try_eat(T![...]);
            ty(p);
            p.finish();
            if !p.try_eat(T![,]) {
                break;
            }
        }
        p.expect(T![']']);
        p.finish();
    } else if p.at(T![fn]) {
        p.start(SyntaxKind::LambdaType);
        p.expect(T![fn]);
        if p.at(T![<]) {
            generic_parameters(p);
        }
        p.expect(T!['(']);
        while !p.at(T![')']) {
            p.start(SyntaxKind::LambdaParameter);
            p.try_eat(T![...]);
            p.expect(SyntaxKind::Ident);
            p.expect(T![:]);
            ty(p);
            p.finish();
            if !p.try_eat(T![,]) {
                break;
            }
        }
        p.expect(T![')']);
        p.expect(T![->]);
        ty(p);
        p.finish();
    } else {
        p.skip();
        return;
    }

    if allow_union && p.at(T![|]) {
        p.start_at(cp, SyntaxKind::UnionType);
        while p.try_eat(T![|]) {
            ty_inner(p, false);
        }
        p.finish();
    }
}

fn path_type(p: &mut Parser) {
    p.start(SyntaxKind::PathType);
    path_type_segment(p, true);
    while p.at(T![::]) {
        path_type_segment(p, false);
    }
    p.finish();
}

fn path_type_segment(p: &mut Parser, first: bool) {
    p.start(SyntaxKind::PathSegment);
    if first {
        p.try_eat(T![::]);
    } else {
        p.expect(T![::]);
    }
    if !p.try_eat(T![super]) {
        p.expect(SyntaxKind::Ident);
    }
    if p.at(T![<]) {
        generic_arguments(p);
    }
    p.finish();
}

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

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

    use super::*;

    #[test]
    fn test_path_type() {
        check(
            ty,
            "String",
            expect![[r#"
                PathType@0..6
                  PathSegment@0..6
                    Ident@0..6 "String"
            "#]],
            expect![],
        );
    }

    #[test]
    fn test_union_type() {
        check(
            ty,
            "String | Int",
            expect![[r#"
                UnionType@0..12
                  PathType@0..7
                    PathSegment@0..7
                      Ident@0..6 "String"
                      Whitespace@6..7 " "
                  BitwiseOr@7..8 "|"
                  Whitespace@8..9 " "
                  PathType@9..12
                    PathSegment@9..12
                      Ident@9..12 "Int"
            "#]],
            expect![],
        );

        check(
            ty,
            "String | Int | Bool",
            expect![[r#"
                UnionType@0..19
                  PathType@0..7
                    PathSegment@0..7
                      Ident@0..6 "String"
                      Whitespace@6..7 " "
                  BitwiseOr@7..8 "|"
                  Whitespace@8..9 " "
                  PathType@9..13
                    PathSegment@9..13
                      Ident@9..12 "Int"
                      Whitespace@12..13 " "
                  BitwiseOr@13..14 "|"
                  Whitespace@14..15 " "
                  PathType@15..19
                    PathSegment@15..19
                      Ident@15..19 "Bool"
            "#]],
            expect![],
        );
    }
}