use alloc::{format, string::String}; use crate::{ ast::{dump::dump, Ast, NodeId}, lexer::{lex, Lexer}, parser::expr, source::SourceCode, token::Lexis, }; use super::{toplevel, Parser, ParserLimits}; fn parse(s: &str, f: fn(&mut Parser)) -> (Ast, NodeId) { let mut lexer = Lexer::new(Lexis::new(1024), SourceCode::unlimited_len(s)); lex(&mut lexer).expect("too many tokens"); let mut parser = Parser::new(&lexer.lexis, &ParserLimits { max_events: 1024 }); f(&mut parser); if !parser.diagnostics.is_empty() { panic!( "parser: {parser:#?}\nemitted diagnostics: {:#?}", parser.diagnostics ); } let mut ast = Ast::new(1024); let (root, _) = parser.into_ast(&mut ast).unwrap(); (ast, root) } fn ast(s: &str, f: fn(&mut Parser)) -> String { let (ast, root) = parse(s, f); // The extra newline is mostly so that it's easier to make the string literals look nice. format!("\n{}", dump(&ast, root, None)) } #[track_caller] fn assert_ast_eq(s: &str, f: fn(&mut Parser), ast_s: &str) { let got = ast(s, f); if ast_s != got { panic!("AST mismatch. expected:\n{ast_s}\n\ngot:\n{got}\n"); } } #[test] fn one_literals() { assert_ast_eq( "1", expr, " Number @ 0..1 Token @ 0..1", ); assert_ast_eq( "ExampleTag123", expr, " Tag @ 0..13 Token @ 0..13", ); assert_ast_eq( "example_ident123", expr, " Ident @ 0..16 Token @ 0..16", ); assert_ast_eq( "#000", expr, " Color @ 0..4 Token @ 0..4", ); assert_ast_eq( "#000F", expr, " Color @ 0..5 Token @ 0..5", ); assert_ast_eq( "#058EF0", expr, " Color @ 0..7 Token @ 0..7", ); assert_ast_eq( "#058EF0FF", expr, " Color @ 0..9 Token @ 0..9", ); } #[test] fn list() { assert_ast_eq( "[]", expr, " List @ 0..2 Token @ 0..1 Token @ 1..2", ); assert_ast_eq( "[1]", expr, " List @ 0..3 Token @ 0..1 Number @ 1..2 Token @ 1..2 Token @ 2..3", ); assert_ast_eq( "[1, 2]", expr, " List @ 0..6 Token @ 0..1 Number @ 1..2 Token @ 1..2 Token @ 2..3 Number @ 4..5 Token @ 4..5 Token @ 5..6", ); assert_ast_eq( "[ 1 2 ]", expr, " List @ 0..42 Token @ 0..1 Token @ 1..2 Number @ 15..16 Token @ 15..16 Token @ 16..17 Number @ 30..31 Token @ 30..31 Token @ 31..32 Token @ 41..42", ); } #[test] fn unary() { assert_ast_eq( "-1", expr, " Unary @ 0..2 Op @ 0..1 Token @ 0..1 Number @ 1..2 Token @ 1..2", ); assert_ast_eq( "!1", expr, " Unary @ 0..2 Op @ 0..1 Token @ 0..1 Number @ 1..2 Token @ 1..2", ); } #[test] fn binary_single() { assert_ast_eq( "1 + 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 - 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 * 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 / 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 < 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 > 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); assert_ast_eq( "1 == 1", expr, " Binary @ 0..6 Number @ 0..1 Token @ 0..1 Op @ 2..4 Token @ 2..4 Number @ 5..6 Token @ 5..6", ); assert_ast_eq( "1 != 1", expr, " Binary @ 0..6 Number @ 0..1 Token @ 0..1 Op @ 2..4 Token @ 2..4 Number @ 5..6 Token @ 5..6", ); assert_ast_eq( "1 <= 1", expr, " Binary @ 0..6 Number @ 0..1 Token @ 0..1 Op @ 2..4 Token @ 2..4 Number @ 5..6 Token @ 5..6", ); assert_ast_eq( "1 >= 1", expr, " Binary @ 0..6 Number @ 0..1 Token @ 0..1 Op @ 2..4 Token @ 2..4 Number @ 5..6 Token @ 5..6", ); assert_ast_eq( "1 = 1", expr, " Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5", ); } #[test] fn binary_precedence() { assert_ast_eq( "1 + 1 + 1", expr, " Binary @ 0..9 Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9", ); assert_ast_eq( "1 * 1 + 1", expr, " Binary @ 0..9 Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9", ); assert_ast_eq( "1 + 1 * 1", expr, " Binary @ 0..9 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Binary @ 4..9 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9", ); assert_ast_eq( "1 < 1 + 1", expr, " Binary @ 0..9 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Binary @ 4..9 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9", ); assert_ast_eq( "1 + 1 < 1", expr, " Binary @ 0..9 Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9", ); assert_ast_eq( "1 + 1 * 1 < 1", expr, " Binary @ 0..13 Binary @ 0..9 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Binary @ 4..9 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9 Op @ 10..11 Token @ 10..11 Number @ 12..13 Token @ 12..13", ); assert_ast_eq( "1 * 1 + 1 < 1", expr, " Binary @ 0..13 Binary @ 0..9 Binary @ 0..5 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Number @ 8..9 Token @ 8..9 Op @ 10..11 Token @ 10..11 Number @ 12..13 Token @ 12..13", ); } #[test] fn binary_cont() { assert_ast_eq( "1 + 1", expr, " Binary @ 0..16 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Token @ 3..4 Number @ 15..16 Token @ 15..16", ); assert_ast_eq( "1 + 1", expr, " Binary @ 0..17 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Token @ 3..4 Number @ 16..17 Token @ 16..17", ); } #[test] fn paren_empty() { assert_ast_eq( "()", expr, " ParenEmpty @ 0..2 Token @ 0..1 Token @ 1..2", ); } #[test] fn paren() { assert_ast_eq( "(1)", expr, " Paren @ 0..3 Token @ 0..1 Number @ 1..2 Token @ 1..2 Token @ 2..3", ); assert_ast_eq( "(1 + 1) * 1", expr, " Binary @ 0..11 Paren @ 0..7 Token @ 0..1 Binary @ 1..6 Number @ 1..2 Token @ 1..2 Op @ 3..4 Token @ 3..4 Number @ 5..6 Token @ 5..6 Token @ 6..7 Op @ 8..9 Token @ 8..9 Number @ 10..11 Token @ 10..11", ); assert_ast_eq( "1 * (1 + 1)", expr, " Binary @ 0..11 Number @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Paren @ 4..11 Token @ 4..5 Binary @ 5..10 Number @ 5..6 Token @ 5..6 Op @ 7..8 Token @ 7..8 Number @ 9..10 Token @ 9..10 Token @ 10..11", ); assert_ast_eq( "( 1 + 1 )", expr, " Paren @ 0..47 Token @ 0..1 Token @ 1..2 Binary @ 15..33 Number @ 15..16 Token @ 15..16 Op @ 17..18 Token @ 17..18 Token @ 18..19 Number @ 32..33 Token @ 32..33 Token @ 36..37 Token @ 46..47", ); } #[test] fn infix_call() { assert_ast_eq( "f x y", toplevel, " Toplevel @ 0..5 Call @ 0..5 Ident @ 0..1 Token @ 0..1 Ident @ 2..3 Token @ 2..3 Ident @ 4..5 Token @ 4..5", ); assert_ast_eq( "sin 1 + cos 2", toplevel, " Toplevel @ 0..13 Binary @ 0..13 Call @ 0..5 Ident @ 0..3 Token @ 0..3 Number @ 4..5 Token @ 4..5 Op @ 6..7 Token @ 6..7 Call @ 8..13 Ident @ 8..11 Token @ 8..11 Number @ 12..13 Token @ 12..13", ); } #[test] fn infix_call_unary_arg() { assert_ast_eq( // NOTE: The whitespace here is misleading. // This is a binary `-`. "f -1", toplevel, " Toplevel @ 0..4 Binary @ 0..4 Ident @ 0..1 Token @ 0..1 Op @ 2..3 Token @ 2..3 Number @ 3..4 Token @ 3..4", ); assert_ast_eq( "f (-1)", toplevel, " Toplevel @ 0..6 Call @ 0..6 Ident @ 0..1 Token @ 0..1 Paren @ 2..6 Token @ 2..3 Unary @ 3..5 Op @ 3..4 Token @ 3..4 Number @ 4..5 Token @ 4..5 Token @ 5..6", ); } #[test] fn lambda() { assert_ast_eq( r#" \_ -> () "#, toplevel, " Toplevel @ 1..9 Lambda @ 1..9 Token @ 1..2 Params @ 2..3 Param @ 2..3 Token @ 2..3 Token @ 4..6 ParenEmpty @ 7..9 Token @ 7..8 Token @ 8..9", ); assert_ast_eq( r#" \x -> x "#, toplevel, " Toplevel @ 1..8 Lambda @ 1..8 Token @ 1..2 Params @ 2..3 Param @ 2..3 Token @ 2..3 Token @ 4..6 Ident @ 7..8 Token @ 7..8", ); assert_ast_eq( r#" \x, y -> x + y "#, toplevel, " Toplevel @ 1..15 Lambda @ 1..15 Token @ 1..2 Params @ 2..6 Param @ 2..3 Token @ 2..3 Token @ 3..4 Param @ 5..6 Token @ 5..6 Token @ 7..9 Binary @ 10..15 Ident @ 10..11 Token @ 10..11 Op @ 12..13 Token @ 12..13 Ident @ 14..15 Token @ 14..15", ); assert_ast_eq( r#" \x, y -> x + y "#, toplevel, " Toplevel @ 1..29 Lambda @ 1..29 Token @ 1..2 Params @ 2..6 Param @ 2..3 Token @ 2..3 Token @ 3..4 Param @ 5..6 Token @ 5..6 Token @ 7..9 Token @ 9..10 Binary @ 24..29 Ident @ 24..25 Token @ 24..25 Op @ 26..27 Token @ 26..27 Ident @ 28..29 Token @ 28..29", ); assert_ast_eq( r#" f \x -> g \y -> x + y "#, toplevel, " Toplevel @ 1..22 Call @ 1..22 Ident @ 1..2 Token @ 1..2 Lambda @ 3..22 Token @ 3..4 Params @ 4..5 Param @ 4..5 Token @ 4..5 Token @ 6..8 Call @ 9..22 Ident @ 9..10 Token @ 9..10 Lambda @ 11..22 Token @ 11..12 Params @ 12..13 Param @ 12..13 Token @ 12..13 Token @ 14..16 Binary @ 17..22 Ident @ 17..18 Token @ 17..18 Op @ 19..20 Token @ 19..20 Ident @ 21..22 Token @ 21..22", ); assert_ast_eq( r#" f \x -> g \y -> x + y "#, toplevel, " Toplevel @ 1..48 Call @ 1..48 Ident @ 1..2 Token @ 1..2 Lambda @ 3..48 Token @ 3..4 Params @ 4..5 Param @ 4..5 Token @ 4..5 Token @ 6..8 Token @ 8..9 Call @ 21..48 Ident @ 21..22 Token @ 21..22 Lambda @ 23..48 Token @ 23..24 Params @ 24..25 Param @ 24..25 Token @ 24..25 Token @ 26..28 Token @ 28..29 Binary @ 43..48 Ident @ 43..44 Token @ 43..44 Op @ 45..46 Token @ 45..46 Ident @ 47..48 Token @ 47..48", ); } #[test] fn if_expr() { assert_ast_eq( r#" if (true) 1 else 2 "#, toplevel, " Toplevel @ 1..19 If @ 1..19 Token @ 1..3 Token @ 4..5 Ident @ 5..9 Token @ 5..9 Token @ 9..10 Number @ 11..12 Token @ 11..12 Token @ 13..17 Number @ 18..19 Token @ 18..19", ); assert_ast_eq( r#" if (true) 1 else 2 "#, toplevel, " Toplevel @ 1..63 If @ 1..63 Token @ 1..3 Token @ 4..5 Ident @ 5..9 Token @ 5..9 Token @ 9..10 Token @ 10..11 Number @ 27..28 Token @ 27..28 Token @ 28..29 Token @ 41..45 Token @ 45..46 Number @ 62..63 Token @ 62..63", ); } #[test] fn let_expr() { assert_ast_eq( r#" let x = 1 x "#, toplevel, " Toplevel @ 1..24 Let @ 1..24 Token @ 1..4 Ident @ 5..6 Token @ 5..6 Token @ 7..8 Number @ 9..10 Token @ 9..10 Token @ 10..11 Ident @ 23..24 Token @ 23..24", ); assert_ast_eq( r#" let x = 1 let y = 2 x + y "#, toplevel, " Toplevel @ 1..50 Let @ 1..50 Token @ 1..4 Ident @ 5..6 Token @ 5..6 Token @ 7..8 Number @ 9..10 Token @ 9..10 Token @ 10..11 Let @ 23..50 Token @ 23..26 Ident @ 27..28 Token @ 27..28 Token @ 29..30 Number @ 31..32 Token @ 31..32 Token @ 32..33 Binary @ 45..50 Ident @ 45..46 Token @ 45..46 Op @ 47..48 Token @ 47..48 Ident @ 49..50 Token @ 49..50", ) }