1mod state;
6mod statement;
7mod expr;
8mod literal;
9
10use rmx::prelude::*;
11
12use bct::{
13 input::Source,
14 module_graph::ModuleId,
15 bracer::{Bracer, TreeToken},
16 split,
17 text::{Text, TextSpan},
18 source_map,
19 lexer,
20 bracer,
21};
22
23use datalove_datafun_ast::ast;
24use bct::diagnostic::DiagnosticBuilder;
25use datalove_diagnostic::DiagnosticBuilderExt;
26use state::{Parser, ScriptCounters};
27
28use salsa::Database as Db;
29
30#[salsa::tracked(returns(ref))]
34pub fn parse<'db>(
35 db: &'db dyn Db,
36 source: Source,
37) -> ast::ParseResult<'db> {
38 parse_with_module_id(db, source, None)
39}
40
41pub fn parse_with_module_id<'db>(
45 db: &'db dyn Db,
46 source: Source,
47 module_id: Option<ModuleId<'db>>,
48) -> ast::ParseResult<'db> {
49 let chunk = source_map::basic_source_map(db, source);
50 let source_text = chunk.text(db);
51 let chunk_lex = lexer::lex_chunk(db, chunk);
52 let bracer = bracer::bracer(db, chunk_lex);
53 emit_bracer_errors(db, bracer, source_text);
54 parse_bracer(db, bracer, source_text, module_id)
55}
56
57#[salsa::tracked(returns(copy))]
59pub fn parse_expr<'db>(
60 db: &'db dyn Db,
61 source: Source,
62) -> ast::ExprFun<'db> {
63 let chunk = source_map::basic_source_map(db, source);
64 let source_text = chunk.text(db);
65 let chunk_lex = lexer::lex_chunk(db, chunk);
66 let bracer = bracer::bracer(db, chunk_lex);
67 emit_bracer_errors(db, bracer, source_text);
68 parse_bracer_expr(db, bracer, source_text)
70}
71
72fn parse_bracer_expr<'db>(
73 db: &'db dyn Db,
74 bracer: Bracer<'db>,
75 source_text: bct::text::Text<'db>,
76) -> ast::ExprFun<'db> {
77 let mut parser = Parser::from_branch_with_context(db, bracer.iter(db), source_text, None, None);
79 let expr = parser.parse_expr_full();
80 parser.error_if_not_exhausted();
81 expr
82}
83
84fn emit_bracer_errors<'db>(
86 db: &'db dyn Db,
87 bracer: Bracer<'db>,
88 source_text: Text<'db>,
89) {
90 let chunk = bracer.chunk(db);
91 let tokens = chunk.tokens(db);
92
93 for (token_range, sigil) in bracer.errors(db) {
94 let is_stray_close = token_range.len() == 1;
97
98 if is_stray_close {
99 if let Some(token) = tokens.get(token_range.start) {
101 let span = token.span();
102 let ts = TextSpan::new(source_text, span);
103 DiagnosticBuilder::error(db, &format!("unmatched '{}'", sigil.as_str()))
104 .code("P050")
105 .primary_label(ts, &format!("this '{}' has no matching '{}'",
106 sigil.as_str(), sigil.open_sigil().as_str()))
107 .emit_parse();
108 }
109 } else {
110 if let Some(open_token) = tokens.get(token_range.start) {
112 let span = open_token.span();
113 let ts = TextSpan::new(source_text, span);
114 DiagnosticBuilder::error(db, &format!("unclosed '{}'", sigil.as_str()))
115 .code("P051")
116 .primary_label(ts, &format!("this '{}' is never closed",
117 sigil.as_str()))
118 .emit_parse();
119 }
120 }
121 }
122}
123
124fn parse_bracer<'db>(
125 db: &'db dyn Db,
126 bracer: Bracer<'db>,
127 source_text: Text<'db>,
128 module_id: Option<ModuleId<'db>>,
129) -> ast::ParseResult<'db> {
130 let groups = split::split_lines(db, bracer.iter(db));
133
134 for written in split::stray_delimiters(&groups) {
137 split::stray_delimiter_error(db, source_text, &written, "statements")
138 .code("P032")
139 .emit_parse();
140 }
141
142 let lines = split::nonempty_groups(groups).into_iter();
143 let (statements, spans) = parse_statements(db, lines, source_text, module_id);
144 let parsed = ast::ParsedStatements { statements: std::sync::Arc::new(statements) };
145 ast::ParseResult {
146 parsed,
147 expr_spans: spans.expr_spans,
148 break_spans: spans.break_spans,
149 continue_spans: spans.continue_spans,
150 ret_spans: spans.ret_spans,
151 set_spans: spans.set_spans,
152 fun_spans: spans.fun_spans,
153 type_alias_spans: spans.type_alias_spans,
154 import_spans: spans.import_spans,
155 alias_spans: spans.alias_spans,
156 }
157}
158
159struct ParsedSpans<'db> {
161 expr_spans: Vec<ast::ParseSpanEntry<'db>>,
162 break_spans: Vec<bct::diagnostic::SpanEntry>,
163 continue_spans: Vec<bct::diagnostic::SpanEntry>,
164 ret_spans: Vec<bct::diagnostic::SpanEntry>,
165 set_spans: Vec<bct::diagnostic::SpanEntry>,
166 fun_spans: Vec<bct::diagnostic::SpanEntry>,
167 type_alias_spans: Vec<bct::diagnostic::SpanEntry>,
168 import_spans: Vec<bct::diagnostic::SpanEntry>,
169 alias_spans: Vec<bct::diagnostic::SpanEntry>,
170}
171
172fn parse_statements<'db>(
176 db: &'db dyn Db,
177 lines: impl Iterator<Item = Vec<TreeToken<'db>>>,
178 source_text: bct::text::Text<'db>,
179 module_id: Option<ModuleId<'db>>,
180) -> (Vec<ast::Statement<'db>>, ParsedSpans<'db>) {
181 let mut statements = vec![];
182 let mut all_expr_spans = vec![];
183 let mut all_break_spans = vec![];
184 let mut all_continue_spans = vec![];
185 let mut all_ret_spans = vec![];
186 let mut all_set_spans = vec![];
187 let mut all_fun_spans = vec![];
188 let mut all_type_alias_spans = vec![];
189 let mut all_import_spans = vec![];
190 let mut all_alias_spans = vec![];
191 let mut line_iter = lines.enumerate().peekable();
192 let mut counters = ScriptCounters::default();
193
194 while let Some((_line_num, line)) = line_iter.next() {
195 let mut parser = Parser::new(db, line, source_text, module_id, counters);
196 let statement = parser.parse_statement(&mut line_iter);
197 counters = parser.script_counters();
198 statements.push(statement);
199 all_expr_spans.extend(parser.take_expr_spans());
200 all_break_spans.extend(parser.take_break_spans());
201 all_continue_spans.extend(parser.take_continue_spans());
202 all_ret_spans.extend(parser.take_ret_spans());
203 all_set_spans.extend(parser.take_set_spans());
204 all_fun_spans.extend(parser.take_fun_spans());
205 all_type_alias_spans.extend(parser.take_type_alias_spans());
206 all_import_spans.extend(parser.take_import_spans());
207 all_alias_spans.extend(parser.take_alias_spans());
208 }
209
210 let spans = ParsedSpans {
211 expr_spans: all_expr_spans,
212 break_spans: all_break_spans,
213 continue_spans: all_continue_spans,
214 ret_spans: all_ret_spans,
215 set_spans: all_set_spans,
216 fun_spans: all_fun_spans,
217 type_alias_spans: all_type_alias_spans,
218 import_spans: all_import_spans,
219 alias_spans: all_alias_spans,
220 };
221 (statements, spans)
222}
223
224#[salsa::tracked(returns(ref))]
226pub fn parse_for_test<'db>(
227 db: &'db dyn Db,
228 source: Source,
229) -> ast::ParsedStatements<'db> {
230 parse(db, source).parsed.clone()
231}
232
233#[salsa::tracked(returns(ref))]
237pub fn parse_integration_test<'db>(
238 db: &'db dyn Db,
239 source: Source,
240) -> ast::ParsedStatements<'db> {
241 parse(db, source).parsed.clone()
242}
243
244#[salsa::tracked(returns(ref))]
250pub fn parse_for_diagnostics<'db>(
251 db: &'db dyn Db,
252 source: Source,
253) -> ast::ParsedStatements<'db> {
254 parse(db, source).parsed.clone()
255}
256
257use datalove_datafun_ast::spans::{SpanMapEntry, DatafunSpans};
258
259pub fn datafun_spans<'db>(
263 db: &'db dyn Db,
264 source: Source,
265) -> DatafunSpans {
266 use bct::diagnostic::SpanEntry;
267
268 let parse_result = parse(db, source);
269 let entries: Vec<SpanMapEntry> = parse_result.expr_spans
270 .iter()
271 .map(|e| SpanMapEntry {
272 expr_key: e.expr_key,
273 entry: SpanEntry::new(e.source, e.span.C()),
274 })
275 .collect();
276
277 DatafunSpans::with_stmt_spans(
278 entries,
279 parse_result.break_spans.C(),
280 parse_result.continue_spans.C(),
281 parse_result.ret_spans.C(),
282 parse_result.set_spans.C(),
283 parse_result.fun_spans.C(),
284 parse_result.type_alias_spans.C(),
285 parse_result.import_spans.C(),
286 parse_result.alias_spans.C(),
287 )
288}
289
290#[salsa::tracked(returns(ref))]
302pub fn parse_module_ast<'db>(
303 db: &'db dyn Db,
304 module: bct::module_graph::Module<'db>,
305) -> ast::ParsedStatements<'db> {
306 parse_module_full(db, module).parsed.clone()
307}
308
309#[salsa::tracked(returns(ref))]
316pub fn module_spans<'db>(
317 db: &'db dyn Db,
318 module: bct::module_graph::Module<'db>,
319) -> DatafunSpans<'db> {
320 let full = parse_module_full(db, module);
321 DatafunSpans::with_stmt_spans(
322 full.expr_spans.iter().map(|e| SpanMapEntry {
323 expr_key: e.expr_key,
324 entry: bct::diagnostic::SpanEntry::new(e.source, e.span.clone()),
325 }).collect(),
326 full.break_spans.clone(),
327 full.continue_spans.clone(),
328 full.ret_spans.clone(),
329 full.set_spans.clone(),
330 full.fun_spans.clone(),
331 full.type_alias_spans.clone(),
332 full.import_spans.clone(),
333 full.alias_spans.clone(),
334 )
335}
336
337#[salsa::tracked(returns(ref))]
342pub fn parse_module_full<'db>(
343 db: &'db dyn Db,
344 module: bct::module_graph::Module<'db>,
345) -> ast::ParseResult<'db> {
346 let module_id = module.id(db);
347 datalove_ct::query_log::log_query("parse", module_id.path(db), datalove_ct::query_log::QueryPhase::Start);
348 let result = parse_with_module_id(db, module.source(db), Some(module_id));
349 datalove_ct::query_log::log_query("parse", module_id.path(db), datalove_ct::query_log::QueryPhase::End);
350 result
351}