1use crate::diagnostic::{Diagnostic, DiagnosticView};
14use crate::parser::{run_parse, run_parse_text};
15use crate::semantic::{lower_into as lower_semantic_into, SemanticModel, SemanticView};
16use crate::source::{SourceFileInput, SourceStore};
17use crate::span::SourceId;
18use crate::tables::CstTables;
19use crate::view::CstView;
20use crate::workspace::ParseWorkspace;
21
22#[derive(Debug, Clone, Copy)]
24pub struct ParseOptions {
25 pub recovery: bool,
28 pub parse_semantic: bool,
30 pub collect_trivia: bool,
32}
33
34impl Default for ParseOptions {
35 fn default() -> Self {
36 Self {
37 recovery: true,
38 parse_semantic: false,
39 collect_trivia: true,
40 }
41 }
42}
43
44#[derive(Debug, Default, Clone)]
46pub struct ParseResult {
47 pub source: SourceId,
48 pub cst: CstTables,
49 pub semantic: Option<SemanticModel>,
50 pub diagnostics: Vec<Diagnostic>,
51}
52
53#[derive(Debug, Clone, Copy)]
55pub struct ParseSessionResult<'a> {
56 pub source: SourceId,
57 pub cst: CstView<'a>,
58 pub semantic: Option<SemanticView<'a>>,
59 pub diagnostics: DiagnosticView<'a>,
60}
61
62#[derive(Debug, Default, Clone)]
64pub struct ParseInput<'a> {
65 pub source: &'a str,
66 pub path: Option<&'a str>,
67 pub locale: Option<&'a str>,
68 pub message_id: Option<&'a str>,
69 pub base_offset: Option<u32>,
70}
71
72#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
78#[non_exhaustive]
79pub enum BatchExecution {
80 #[default]
81 Sequential,
82 Parallel,
85}
86
87#[derive(Debug, Clone, Copy)]
88pub struct BatchParseOptions {
89 pub execution: BatchExecution,
90 pub max_threads: Option<usize>,
92 pub preserve_order: bool,
95 pub parse: ParseOptions,
96}
97
98impl Default for BatchParseOptions {
99 fn default() -> Self {
100 Self {
101 execution: BatchExecution::Sequential,
102 max_threads: None,
103 preserve_order: true,
104 parse: ParseOptions::default(),
105 }
106 }
107}
108
109#[derive(Debug, Default, Clone)]
110pub struct BatchParseResult {
111 pub sources: SourceStore,
112 pub items: Vec<BatchParseItem>,
113 pub execution: BatchExecution,
116 pub degraded: bool,
120}
121
122#[derive(Debug, Default, Clone)]
123pub struct BatchParseItem {
124 pub source: SourceId,
125 pub result: ParseResult,
126}
127
128pub fn parse_source(
130 sources: &SourceStore,
131 source_id: SourceId,
132 options: ParseOptions,
133) -> ParseResult {
134 let mut workspace = ParseWorkspace::new();
135 let source_len = sources.get(source_id).map_or(0, |f| f.text.len());
136 workspace.reserve_for_source_len(source_len);
137
138 run_parse(sources, source_id, &mut workspace, options);
139
140 materialise_owned_workspace(sources, source_id, workspace, options)
141}
142
143pub fn parse_message(source: &str) -> ParseResult {
148 assert!(
149 u32::try_from(source.len()).is_ok(),
150 "source length fits in u32"
151 );
152 let source_id = SourceId::new(0);
153 let mut workspace = ParseWorkspace::new();
154 workspace.reserve_for_source_len(source.len());
155 run_parse_text(source, source_id, &mut workspace, ParseOptions::default());
156 materialise_one_shot_message(source, source_id, workspace)
157}
158
159pub fn parse_source_session<'a>(
162 sources: &'a SourceStore,
163 source_id: SourceId,
164 workspace: &'a mut ParseWorkspace,
165 options: ParseOptions,
166) -> ParseSessionResult<'a> {
167 workspace.clear();
168 run_parse(sources, source_id, workspace, options);
169
170 if options.parse_semantic {
171 let model = workspace
174 .semantic
175 .model
176 .get_or_insert_with(SemanticModel::default);
177 lower_semantic_into(sources, source_id, &workspace.parser.tables, model);
178 } else {
179 workspace.semantic.model = None;
180 }
181
182 let cst = CstView::new(sources, source_id, &workspace.parser.tables);
183 let diagnostics = DiagnosticView {
184 sources,
185 records: &workspace.parser.diagnostics,
186 };
187 let semantic = workspace
188 .semantic
189 .model
190 .as_ref()
191 .map(|m| SemanticView::new(m, &workspace.parser.tables));
192 ParseSessionResult {
193 source: source_id,
194 cst,
195 semantic,
196 diagnostics,
197 }
198}
199
200pub fn parse_batch(inputs: &[ParseInput<'_>], options: BatchParseOptions) -> BatchParseResult {
208 let mut sources = SourceStore::with_capacity(inputs.len());
209 let mut items = Vec::with_capacity(inputs.len());
210 let mut workspace = ParseWorkspace::new();
211
212 for input in inputs {
213 let source_id = sources.add(SourceFileInput {
214 source: input.source,
215 path: input.path,
216 locale: input.locale,
217 message_id: input.message_id,
218 base_offset: input.base_offset,
219 });
220 workspace.clear();
221 workspace.reserve_for_source_len(input.source.len());
222 run_parse(&sources, source_id, &mut workspace, options.parse);
223 let result = materialise(&sources, source_id, &workspace, options.parse);
224 items.push(BatchParseItem {
225 source: source_id,
226 result,
227 });
228 }
229
230 let degraded = !matches!(options.execution, BatchExecution::Sequential);
231
232 BatchParseResult {
233 sources,
234 items,
235 execution: BatchExecution::Sequential,
236 degraded,
237 }
238}
239
240fn materialise(
241 sources: &SourceStore,
242 source_id: SourceId,
243 workspace: &ParseWorkspace,
244 options: ParseOptions,
245) -> ParseResult {
246 let cst = workspace.parser.tables.clone();
247 let semantic = if options.parse_semantic {
248 let mut model = SemanticModel::default();
249 lower_semantic_into(sources, source_id, &cst, &mut model);
250 Some(model)
251 } else {
252 None
253 };
254 let diagnostics = DiagnosticView {
255 sources,
256 records: &workspace.parser.diagnostics,
257 }
258 .iter()
259 .collect();
260 ParseResult {
261 source: source_id,
262 cst,
263 semantic,
264 diagnostics,
265 }
266}
267
268fn materialise_owned_workspace(
269 sources: &SourceStore,
270 source_id: SourceId,
271 mut workspace: ParseWorkspace,
272 options: ParseOptions,
273) -> ParseResult {
274 let cst = core::mem::take(&mut workspace.parser.tables);
275 let semantic = if options.parse_semantic {
276 let mut model = SemanticModel::default();
277 lower_semantic_into(sources, source_id, &cst, &mut model);
278 Some(model)
279 } else {
280 None
281 };
282 let diagnostics = DiagnosticView {
283 sources,
284 records: &workspace.parser.diagnostics,
285 }
286 .iter()
287 .collect();
288 ParseResult {
289 source: source_id,
290 cst,
291 semantic,
292 diagnostics,
293 }
294}
295
296fn materialise_one_shot_message(
297 source: &str,
298 source_id: SourceId,
299 mut workspace: ParseWorkspace,
300) -> ParseResult {
301 let cst = core::mem::take(&mut workspace.parser.tables);
302 let diagnostics = if workspace.parser.diagnostics.is_empty() {
303 Vec::new()
304 } else {
305 let mut sources = SourceStore::with_capacity(1);
306 let actual_source_id = sources.add(SourceFileInput {
307 source,
308 ..Default::default()
309 });
310 debug_assert_eq!(actual_source_id, source_id);
311 DiagnosticView {
312 sources: &sources,
313 records: &workspace.parser.diagnostics,
314 }
315 .iter()
316 .collect()
317 };
318 ParseResult {
319 source: source_id,
320 cst,
321 semantic: None,
322 diagnostics,
323 }
324}