brack-parser 0.1.0

The parser for the Brack markup language
Documentation
use anyhow::Result;
use brack_sdk_rs::ast::AST;
use brack_tokenizer::tokens::Token;

use crate::{
    ast::new_document,
    stmt,
};

pub fn parse(tokens: &Vec<Token>) -> Result<AST> {
    let mut new_tokens = tokens.clone();
    let mut result = new_document();

    while new_tokens.len() > 0 {
        match stmt::parse(&new_tokens) {
            Ok((ast, tokens)) => {
                new_tokens = tokens;
                result.add(ast)?;
            }
            Err(e) => return Err(e),
        }
    }

    Ok(result)
}

#[cfg(test)]
mod test {
    use anyhow::Result;
    use brack_tokenizer::tokens::{mock_location, Token};

    use crate::ast::{
        assert_ast_eq, new_angle_with_children, new_curly_with_children,
        new_document_with_children, new_expr_with_children, new_ident, new_square_with_children,
        new_stmt_with_children, new_text,
    };

    use super::parse;

    #[test]
    fn test_parse_no_commands() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, World!".to_string(), mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![new_text("Hello, World!".to_string())],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_square_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_curly_brackets() -> Result<()> {
        let tokens = vec![
            Token::CurlyBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("Heading".to_string(), mock_location()),
            Token::CurlyBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("Hello, World!".to_string(), mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected = new_document_with_children(vec![
            new_stmt_with_children(vec![new_curly_with_children(vec![
                new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                new_expr_with_children(vec![new_text("Heading".to_string())]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
                "Hello, World!".to_string(),
            )])]),
        ]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_angle_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::AngleBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::AngleBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_angle_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_two_arguments_includes_square_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                        new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_nesting_commands() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_square_with_children(vec![
                            new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                            new_expr_with_children(vec![new_text("World!".to_string())]),
                            new_expr_with_children(vec![new_text(
                                "https://example.com/".to_string(),
                            )]),
                        ])]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_newlines() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello,".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("World,".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::CurlyBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("**".to_string(), mock_location()),
            Token::Text("Contact".to_string(), mock_location()),
            Token::CurlyBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("My website".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("2023.12.28".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected = new_document_with_children(vec![
            new_stmt_with_children(vec![
                new_expr_with_children(vec![new_text("Hello,".to_string())]),
                new_expr_with_children(vec![new_text("World,".to_string())]),
            ]),
            new_stmt_with_children(vec![new_curly_with_children(vec![
                new_ident(vec![
                    new_text("std".to_string()),
                    new_text("**".to_string()),
                ]),
                new_expr_with_children(vec![new_text("Contact".to_string())]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![
                new_square_with_children(vec![
                    new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                    new_expr_with_children(vec![new_text("My website".to_string())]),
                    new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
                ]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
                "2023.12.28".to_string(),
            )])]),
        ]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }
}