Skip to main content

leo_parser_rowan/parser/
grammar.rs

1// Copyright (C) 2019-2026 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17//! Grammar entry points for the Leo parser.
18//!
19//! This module provides the public parsing functions that dispatch to
20//! the appropriate grammar rules.
21
22use super::{Parse, Parser};
23use crate::{lexer::lex, syntax_kind::SyntaxKind::*};
24
25/// Parse a complete Leo source file.
26///
27/// This handles imports followed by a program declaration.
28pub fn parse_file(source: &str) -> Parse {
29    let (tokens, _lex_errors) = lex(source);
30    let mut parser = Parser::new(source, &tokens);
31
32    let root = parser.start();
33    parser.parse_file_items();
34    root.complete(&mut parser, ROOT);
35
36    parser.finish()
37}
38
39/// Parse a single expression.
40pub fn parse_expression_entry(source: &str) -> Parse {
41    let (tokens, _lex_errors) = lex(source);
42    let mut parser = Parser::new(source, &tokens);
43
44    let root = parser.start();
45    parser.parse_expr();
46    root.complete(&mut parser, ROOT);
47
48    parser.finish()
49}
50
51/// Parse module contents (const, struct, inline declarations only).
52pub fn parse_module_entry(source: &str) -> Parse {
53    let (tokens, _lex_errors) = lex(source);
54    let mut parser = Parser::new(source, &tokens);
55
56    let root = parser.start();
57    parser.parse_module_items();
58    root.complete(&mut parser, ROOT);
59
60    parser.finish()
61}
62
63/// Parse a single statement.
64pub fn parse_statement_entry(source: &str) -> Parse {
65    let (tokens, _lex_errors) = lex(source);
66    let mut parser = Parser::new(source, &tokens);
67
68    let root = parser.start();
69    parser.parse_stmt();
70    root.complete(&mut parser, ROOT);
71
72    parser.finish()
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use expect_test::{Expect, expect};
79
80    fn check_file(input: &str, expect: Expect) {
81        let parse = parse_file(input);
82        let output = format!("{:#?}", parse.syntax());
83        expect.assert_eq(&output);
84    }
85
86    #[test]
87    fn parse_file_empty() {
88        check_file("", expect![[r#"
89            ROOT@0..0
90        "#]]);
91    }
92
93    #[test]
94    fn parse_file_trivial() {
95        check_file("program test.aleo { }", expect![[r#"
96                ROOT@0..21
97                  PROGRAM_DECL@0..21
98                    KW_PROGRAM@0..7 "program"
99                    WHITESPACE@7..8 " "
100                    IDENT@8..12 "test"
101                    DOT@12..13 "."
102                    KW_ALEO@13..17 "aleo"
103                    WHITESPACE@17..18 " "
104                    L_BRACE@18..19 "{"
105                    WHITESPACE@19..20 " "
106                    R_BRACE@20..21 "}"
107            "#]]);
108    }
109
110    fn check_module(input: &str, expect: Expect) {
111        let parse = parse_module_entry(input);
112        let output = format!("{:#?}", parse.syntax());
113        expect.assert_eq(&output);
114    }
115
116    fn check_module_no_errors(input: &str) {
117        let parse = parse_module_entry(input);
118        if !parse.errors().is_empty() {
119            for err in parse.errors() {
120                eprintln!("error at {:?}: {}", err.range, err.message);
121            }
122            eprintln!("tree:\n{:#?}", parse.syntax());
123            panic!("module parse had {} error(s)", parse.errors().len());
124        }
125    }
126
127    #[test]
128    fn parse_module_empty() {
129        check_module("", expect![[r#"
130            ROOT@0..0
131        "#]]);
132    }
133
134    #[test]
135    fn parse_module_const() {
136        check_module_no_errors("const X: u32 = 32u32;");
137    }
138
139    #[test]
140    fn parse_module_struct() {
141        check_module_no_errors("struct Data { values: u32, }");
142    }
143
144    #[test]
145    fn parse_module_inline_fn() {
146        check_module_no_errors("inline helper() -> u32 { return 0u32; }");
147    }
148
149    #[test]
150    fn parse_module_mixed_items() {
151        check_module_no_errors(
152            "const X: u32 = 3;\n\
153             struct Data { values: u32, }\n\
154             inline helper() -> u32 { return 0u32; }",
155        );
156    }
157
158    #[test]
159    fn parse_module_with_comments() {
160        // Module sections in test files are separated by comments.
161        // Comments are trivia and should be skipped.
162        check_module_no_errors(
163            "// --- Next Module: dep.leo --- //\n\
164             const X: u32 = 32u32;\n\
165             // --- Next Module: dep/inner.leo --- //\n\
166             const Y: u32 = 64u32;",
167        );
168    }
169
170    #[test]
171    fn parse_file_with_module_sections() {
172        // Multi-section test files: program block followed by module items.
173        let source = "\
174program test.aleo {
175    transition foo() -> u32 { return 0u32; }
176}
177
178// --- Next Module: dep.leo --- //
179
180const X: u32 = 32u32;
181
182// --- Next Module: dep/inner.leo --- //
183
184const Y: u32 = 64u32;";
185
186        let parse = parse_file(source);
187        if !parse.errors().is_empty() {
188            for err in parse.errors() {
189                eprintln!("error at {:?}: {}", err.range, err.message);
190            }
191            eprintln!("tree:\n{:#?}", parse.syntax());
192            panic!("file parse had {} error(s)", parse.errors().len());
193        }
194    }
195}