use super::{Parse, Parser};
use crate::{lexer::lex, syntax_kind::SyntaxKind::*};
pub fn parse_file(source: &str) -> Parse {
let (tokens, lex_errors) = lex(source);
let mut parser = Parser::new(source, &tokens);
let root = parser.start();
parser.parse_file_items();
root.complete(&mut parser, ROOT);
parser.finish(lex_errors)
}
pub fn parse_expression_entry(source: &str) -> Parse {
let (tokens, lex_errors) = lex(source);
let mut parser = Parser::new(source, &tokens);
let root = parser.start();
let errors_before = parser.error_count();
parser.parse_expr();
if !parser.at_eof() && !parser.at(ERROR) && parser.error_count() == errors_before {
let expected: Vec<&str> = Parser::EXPR_CONTINUATION.iter().map(|k| k.user_friendly_name()).collect();
parser.error_unexpected(parser.current(), &expected);
}
root.complete(&mut parser, ROOT);
parser.finish(lex_errors)
}
pub fn parse_module_entry(source: &str) -> Parse {
let (tokens, lex_errors) = lex(source);
let mut parser = Parser::new(source, &tokens);
let root = parser.start();
parser.parse_module_items();
root.complete(&mut parser, ROOT);
parser.finish(lex_errors)
}
pub fn parse_statement_entry(source: &str) -> Parse {
let (tokens, lex_errors) = lex(source);
let mut parser = Parser::new(source, &tokens);
let root = parser.start();
let errors_before = parser.error_count();
parser.parse_stmt();
if !parser.at_eof() && !parser.at(ERROR) && parser.error_count() == errors_before {
let mut expected: Vec<&str> = Parser::EXPR_CONTINUATION.iter().map(|k| k.user_friendly_name()).collect();
expected.push("';'");
parser.error_unexpected(parser.current(), &expected);
}
root.complete(&mut parser, ROOT);
parser.finish(lex_errors)
}
#[cfg(test)]
mod tests {
use super::*;
use expect_test::{Expect, expect};
fn check_file(input: &str, expect: Expect) {
let parse = parse_file(input);
let output = format!("{:#?}", parse.syntax());
expect.assert_eq(&output);
}
#[test]
fn parse_file_empty() {
check_file("", expect![[r#"
ROOT@0..0
"#]]);
}
#[test]
fn parse_file_trivial() {
check_file("program test.aleo { }", expect![[r#"
ROOT@0..21
PROGRAM_DECL@0..21
KW_PROGRAM@0..7 "program"
WHITESPACE@7..8 " "
IDENT@8..12 "test"
DOT@12..13 "."
KW_ALEO@13..17 "aleo"
WHITESPACE@17..18 " "
L_BRACE@18..19 "{"
WHITESPACE@19..20 " "
R_BRACE@20..21 "}"
"#]]);
}
fn check_module(input: &str, expect: Expect) {
let parse = parse_module_entry(input);
let output = format!("{:#?}", parse.syntax());
expect.assert_eq(&output);
}
fn check_module_no_errors(input: &str) {
let parse = parse_module_entry(input);
if !parse.errors().is_empty() {
for err in parse.errors() {
eprintln!("error at {:?}: {}", err.range, err.message);
}
eprintln!("tree:\n{:#?}", parse.syntax());
panic!("module parse had {} error(s)", parse.errors().len());
}
}
#[test]
fn parse_module_empty() {
check_module("", expect![[r#"
ROOT@0..0
"#]]);
}
#[test]
fn parse_module_const() {
check_module_no_errors("const X: u32 = 32u32;");
}
#[test]
fn parse_module_struct() {
check_module_no_errors("struct Data { values: u32, }");
}
#[test]
fn parse_module_inline_fn() {
check_module_no_errors("fn helper() -> u32 { return 0u32; }");
}
#[test]
fn parse_module_mixed_items() {
check_module_no_errors(
"const X: u32 = 3;\n\
struct Data { values: u32, }\n\
fn helper() -> u32 { return 0u32; }",
);
}
#[test]
fn parse_module_with_comments() {
check_module_no_errors(
"// --- Next Module: dep.leo --- //\n\
const X: u32 = 32u32;\n\
// --- Next Module: dep/inner.leo --- //\n\
const Y: u32 = 64u32;",
);
}
#[test]
fn parse_file_full() {
check_file("import credits.aleo;\nprogram test.aleo { fn main() { } }", expect![[r#"
ROOT@0..56
IMPORT@0..20
KW_IMPORT@0..6 "import"
WHITESPACE@6..7 " "
IDENT@7..14 "credits"
DOT@14..15 "."
KW_ALEO@15..19 "aleo"
SEMICOLON@19..20 ";"
LINEBREAK@20..21 "\n"
PROGRAM_DECL@21..56
KW_PROGRAM@21..28 "program"
WHITESPACE@28..29 " "
IDENT@29..33 "test"
DOT@33..34 "."
KW_ALEO@34..38 "aleo"
WHITESPACE@38..39 " "
L_BRACE@39..40 "{"
FUNCTION_DEF@40..54
WHITESPACE@40..41 " "
KW_FN@41..43 "fn"
WHITESPACE@43..44 " "
IDENT@44..48 "main"
PARAM_LIST@48..50
L_PAREN@48..49 "("
R_PAREN@49..50 ")"
WHITESPACE@50..51 " "
BLOCK@51..54
L_BRACE@51..52 "{"
WHITESPACE@52..53 " "
R_BRACE@53..54 "}"
WHITESPACE@54..55 " "
R_BRACE@55..56 "}"
"#]]);
}
#[test]
fn parse_module_recovery() {
let parse = parse_module_entry("42 struct Foo { x: u32, }");
assert!(!parse.errors().is_empty(), "expected errors from unrecognized token");
let tree = format!("{:#?}", parse.syntax());
assert!(tree.contains("STRUCT_DEF"), "expected struct to be recovered, got:\n{tree}");
}
#[test]
fn parse_file_with_module_sections() {
let source = "\
program test.aleo {
fn foo() -> u32 { return 0u32; }
}
// --- Next Module: dep.leo --- //
const X: u32 = 32u32;
// --- Next Module: dep/inner.leo --- //
const Y: u32 = 64u32;";
let parse = parse_file(source);
if !parse.errors().is_empty() {
for err in parse.errors() {
eprintln!("error at {:?}: {}", err.range, err.message);
}
eprintln!("tree:\n{:#?}", parse.syntax());
panic!("file parse had {} error(s)", parse.errors().len());
}
}
}