lutra_compiler/parser/
mod.rs1mod def;
2mod expr;
3mod helpers;
4mod interpolation;
5mod lexer;
6mod test;
7mod types;
8
9pub use self::lexer::{Token, TokenKind, lex_source_recovery};
10
11use chumsky::input::Input as _;
12use chumsky::prelude::*;
13
14use crate::Span;
15use crate::diagnostic::Diagnostic;
16use crate::pr;
17
18pub fn is_submodule(source: &str) -> Result<bool, Vec<Diagnostic>> {
19 let mut tokens = lexer::lex_source(source)?;
20 Ok(tokens
21 .next()
22 .is_some_and(|t| matches!(t.kind, TokenKind::Keyword("submodule"))))
23}
24
25pub fn parse_source(
26 source: &str,
27 source_id: u16,
28) -> (Option<pr::Source>, Vec<Diagnostic>, Vec<Token>) {
29 let (tokens, mut errors) = lexer::lex_source_recovery(source, source_id);
30
31 let mut trivia = Vec::new();
32
33 let ast = if let Some(tokens) = tokens {
34 trivia = tokens.trivia;
35 let tokens = prepare_tokens(tokens.semantic, source_id);
36
37 let (ast, errs) = def::source().parse(tokens.as_input()).into_output_errors();
38
39 let mut parsed = ast;
40 if let Some(parsed) = &mut parsed {
41 parsed.span.start = 0;
42 parsed.span.len = source.len() as u16;
43 }
44
45 errors.extend(errs.into_iter().map(Diagnostic::from));
46
47 parsed
48 } else {
49 None
50 };
51
52 (ast, errors, trivia)
53}
54
55pub fn parse_expr(source: &str, source_id: u16) -> (Option<pr::Expr>, Vec<Diagnostic>) {
56 let (tokens, mut errors) = lexer::lex_source_recovery(source, source_id);
57
58 let ast = if let Some(tokens) = tokens {
59 let tokens = prepare_tokens(tokens.semantic, source_id);
60
61 let (ast, errs) = expr::expr(types::type_expr())
62 .parse(tokens.as_input())
63 .into_output_errors();
64 errors.extend(errs.into_iter().map(Diagnostic::from));
65 ast
66 } else {
67 None
68 };
69
70 (ast, errors)
71}
72
73pub fn parse_path(source: &str) -> Option<pr::Path> {
74 let tokens = lexer::lex_source(source).ok()?;
75 let tokens = prepare_tokens(tokens.collect(), 0);
76
77 let (path, _) = expr::path().parse(tokens.as_input()).into_output_errors();
78 path
79}
80
81fn prepare_tokens(tokens: Vec<lexer::Token>, source_id: u16) -> ParserTokens {
83 let pairs: Vec<(TokenKind, Span)> = tokens
84 .into_iter()
85 .map(|t| (t.kind, t.span.with_source_id(source_id)))
86 .collect();
87
88 let eoi = Span {
89 start: pairs.last().map(|(_, s)| s.end()).unwrap_or(0),
90 len: 0,
91 source_id,
92 };
93
94 ParserTokens { pairs, eoi }
95}
96
97struct ParserTokens {
98 pairs: Vec<(TokenKind, Span)>,
99 eoi: Span,
100}
101
102impl ParserTokens {
103 fn as_input(&self) -> PInput<'_> {
104 self.pairs.as_slice().split_token_span(self.eoi)
105 }
106}
107
108pub(crate) type PError<'src> = Rich<'src, lexer::TokenKind, Span>;
111
112pub(crate) type PExtra<'src> = chumsky::extra::Err<PError<'src>>;
114
115type PInput<'src> =
120 chumsky::input::MappedInput<'src, lexer::TokenKind, Span, &'src [(lexer::TokenKind, Span)]>;