litcheck_filecheck/check/
program.rs1use std::collections::VecDeque;
2
3use crate::{
4 ast::*,
5 common::*,
6 errors::InvalidCheckFileError,
7 pattern::matcher::{MatchAll, MatchAny, SimpleMatcher},
8 rules::*,
9};
10
11#[derive(Debug)]
16pub enum CheckTree<'a> {
17 Leaf(CheckGroup<'a>),
19 Both {
21 root: MatchAny<'a>,
23 left: Box<CheckTree<'a>>,
25 right: Box<CheckTree<'a>>,
27 },
28 Left {
30 root: MatchAny<'a>,
32 left: Box<CheckTree<'a>>,
34 },
35 Right {
37 root: MatchAny<'a>,
39 right: Box<CheckTree<'a>>,
41 },
42}
43impl<'a> CheckTree<'a> {
44 pub fn leftmost(&self) -> Either<&CheckGroup<'a>, &MatchAny<'a>> {
45 match self {
46 Self::Leaf(ref group) => Left(group),
47 Self::Both { ref left, .. } | Self::Left { ref left, .. } => left.leftmost(),
48 Self::Right { ref root, .. } => Right(root),
49 }
50 }
51
52 pub fn rightmost(&self) -> Either<&CheckGroup<'a>, &MatchAny<'a>> {
53 match self {
54 Self::Leaf(ref group) => Left(group),
55 Self::Both { ref right, .. } | Self::Right { ref right, .. } => right.rightmost(),
56 Self::Left { ref root, .. } => Right(root),
57 }
58 }
59}
60
61#[derive(Debug)]
62pub enum CheckGroup<'a> {
63 Never(MatchAny<'a>),
68 Unordered(Box<CheckDag<'a>>),
74 Bounded {
79 left: Box<CheckDag<'a>>,
81 right: Box<CheckGroup<'a>>,
84 },
85 Ordered(Vec<Box<dyn DynRule + 'a>>),
88 Repeated {
90 rule: Box<dyn DynRule + 'a>,
91 count: usize,
92 },
93 Tree(Box<CheckTree<'a>>),
97}
98impl<'a> CheckGroup<'a> {
99 pub fn first_pattern_span(&self) -> SourceSpan {
100 match self {
101 Self::Never(match_any) => match_any.first_pattern_span(),
102 Self::Unordered(check_dag) => check_dag.first_pattern_span(),
103 Self::Bounded { left, .. } => left.first_pattern_span(),
104 Self::Ordered(rules) => rules
105 .iter()
106 .map(|rule| rule.span())
107 .min_by_key(|span| span.start())
108 .unwrap(),
109 Self::Repeated { rule, .. } => rule.span(),
110 Self::Tree(tree) => match tree.leftmost() {
111 Left(group) => group.first_pattern_span(),
112 Right(match_any) => match_any.first_pattern_span(),
113 },
114 }
115 }
116
117 pub fn span(&self) -> SourceSpan {
118 match self {
119 Self::Never(match_any) => match_any.span(),
120 Self::Unordered(check_dag) => check_dag.span(),
121 Self::Bounded { left, right } => {
122 let start = left.span().start();
123 let end = right.span().end();
124 SourceSpan::from(start..end)
125 }
126 Self::Ordered(rules) => {
127 let start = rules[0].span().start();
128 let end = rules.last().unwrap().span().end();
129 SourceSpan::from(start..end)
130 }
131 Self::Repeated { rule, .. } => rule.span(),
132 Self::Tree(ref tree) => {
133 let leftmost_start = match tree.leftmost() {
134 Left(left) => left.span().start(),
135 Right(left) => left.span().start(),
136 };
137 let rightmost_end = match tree.rightmost() {
138 Left(right) => right.span().end(),
139 Right(right) => right.span().end(),
140 };
141 SourceSpan::from(leftmost_start..rightmost_end)
142 }
143 }
144 }
145}
146
147#[derive(Debug)]
148pub enum CheckSection<'a> {
149 Block {
155 label: SimpleMatcher<'a>,
157 body: Vec<CheckGroup<'a>>,
158 },
159 Group { body: CheckGroup<'a> },
173}
174
175#[derive(Default, Debug)]
176pub struct CheckProgram<'a> {
177 pub sections: Vec<CheckSection<'a>>,
178}
179impl<'a> CheckProgram<'a> {
180 pub fn compile(
181 check_file: CheckFile<'a>,
182 config: &Config,
183 interner: &mut StringInterner,
184 ) -> DiagResult<Self> {
185 let lines = check_file.into_lines();
186 if lines.is_empty() {
187 return Err(Report::from(InvalidCheckFileError::Empty));
188 }
189
190 let mut program = Self::default();
191 program.compile_lines(lines, config, interner)?;
192
193 Ok(program)
194 }
195
196 fn compile_lines(
198 &mut self,
199 lines: Vec<CheckLine<'a>>,
200 config: &Config,
201 interner: &mut StringInterner,
202 ) -> DiagResult<()> {
203 let mut iter = lines.into_iter().peekable();
205 let mut label = None;
206 let mut block = vec![];
207 let mut blocks: VecDeque<(Option<CheckLine<'a>>, Vec<CheckLine<'a>>)> = VecDeque::default();
208 while let Some(next) = iter.peek() {
209 match next.kind() {
210 Check::Label => {
211 if !block.is_empty() {
212 blocks.push_back((label.take(), core::mem::take(&mut block)));
213 }
214 label = iter.next();
215 while let Some(next) = iter.peek() {
216 match next.kind() {
217 Check::Label => {
218 break;
219 }
220 _ => {
221 block.push(iter.next().unwrap());
222 }
223 }
224 }
225 }
226 Check::Empty | Check::Same | Check::Next if block.is_empty() && label.is_none() => {
227 return Err(Report::from(InvalidCheckFileError::InvalidFirstCheck {
228 kind: Check::Empty,
229 line: next.span(),
230 }));
231 }
232 Check::Plain
233 | Check::Count(_)
234 | Check::Next
235 | Check::Same
236 | Check::Not
237 | Check::Dag
238 | Check::Empty => {
239 block.push(iter.next().unwrap());
240 }
241 _ => unreachable!(),
242 }
243 }
244
245 if !block.is_empty() {
246 blocks.push_back((label.take(), block));
247 }
248
249 self.compile_blocks(&mut blocks, config, interner)
250 }
251
252 fn compile_blocks(
254 &mut self,
255 blocks: &mut VecDeque<(Option<CheckLine<'a>>, Vec<CheckLine<'a>>)>,
256 config: &Config,
257 interner: &mut StringInterner,
258 ) -> DiagResult<()> {
259 let mut groups = vec![];
260 let mut pending_tree = None;
261
262 while let Some((maybe_label, body)) = blocks.pop_front() {
263 let mut body = VecDeque::from(body);
264 while let Some(line) = body.pop_front() {
265 match line.kind() {
266 Check::Not => {
267 assert!(pending_tree.is_none());
268 let mut nots = vec![line];
269 while let Some(next) = body.pop_front() {
270 if matches!(next.kind(), Check::Not) {
271 nots.push(next);
272 } else {
273 body.push_front(next);
274 break;
275 }
276 }
277 let matcher = MatchAny::from(MatchAll::compile(nots, config, interner)?);
278 if body.is_empty() {
279 match groups.pop() {
280 Some(CheckGroup::Tree(left)) => {
281 groups.push(CheckGroup::Tree(Box::new(CheckTree::Left {
282 root: matcher,
283 left,
284 })))
285 }
286 Some(left @ CheckGroup::Unordered(_)) => {
287 groups.push(CheckGroup::Tree(Box::new(CheckTree::Left {
288 root: matcher,
289 left: Box::new(CheckTree::Leaf(left)),
290 })))
291 }
292 Some(group) => {
293 groups.push(group);
294 groups.push(CheckGroup::Never(matcher));
295 }
296 None => groups.push(CheckGroup::Never(matcher)),
297 }
298 } else {
299 match groups.pop() {
300 Some(CheckGroup::Tree(left)) => {
301 pending_tree = Some((Some(left), matcher));
302 }
303 Some(left @ CheckGroup::Unordered(_)) => {
304 let left = Box::new(CheckTree::Leaf(left));
305 pending_tree = Some((Some(left), matcher));
306 }
307 Some(group) => {
308 groups.push(group);
309 pending_tree = Some((None, matcher));
310 }
311 None => {
312 pending_tree = Some((None, matcher));
313 }
314 }
315 }
316 }
317 Check::Dag => {
318 let mut dags = vec![line];
319 while let Some(next) = body.pop_front() {
320 if matches!(next.kind(), Check::Dag) {
321 dags.push(next);
322 } else {
323 body.push_front(next);
324 break;
325 }
326 }
327 let check_dag =
328 Box::new(CheckDag::new(MatchAll::compile(dags, config, interner)?));
329
330 let group = if matches!(
331 body.front().map(|line| line.kind()),
332 Some(Check::Plain | Check::Count(_))
333 ) {
334 let line = body.pop_front().unwrap();
335 let bounding_group = match line.kind() {
336 Check::Plain => CheckGroup::Ordered(vec![self.compile_rule(
337 line.ty,
338 line.pattern,
339 config,
340 interner,
341 )?]),
342 Check::Count(count) => CheckGroup::Repeated {
343 rule: self.compile_rule(
344 line.ty,
345 line.pattern,
346 config,
347 interner,
348 )?,
349 count,
350 },
351 _ => unsafe { std::hint::unreachable_unchecked() },
352 };
353 CheckGroup::Bounded {
354 left: check_dag,
355 right: Box::new(bounding_group),
356 }
357 } else {
358 CheckGroup::Unordered(check_dag)
359 };
360 if let Some((maybe_left, root)) = pending_tree.take() {
361 match maybe_left {
362 Some(left) => {
363 groups.push(CheckGroup::Tree(Box::new(CheckTree::Both {
364 root,
365 left,
366 right: Box::new(CheckTree::Leaf(group)),
367 })));
368 }
369 None => {
370 groups.push(CheckGroup::Tree(Box::new(CheckTree::Right {
371 root,
372 right: Box::new(CheckTree::Leaf(group)),
373 })));
374 }
375 }
376 } else {
377 groups.push(group);
378 }
379 }
380 Check::Count(count) => {
381 let group = CheckGroup::Repeated {
382 rule: self.compile_rule(line.ty, line.pattern, config, interner)?,
383 count,
384 };
385 if let Some((maybe_left, root)) = pending_tree.take() {
386 match maybe_left {
387 Some(left) => {
388 groups.push(CheckGroup::Tree(Box::new(CheckTree::Both {
389 root,
390 left,
391 right: Box::new(CheckTree::Leaf(group)),
392 })));
393 }
394 None => {
395 groups.push(CheckGroup::Tree(Box::new(CheckTree::Right {
396 root,
397 right: Box::new(CheckTree::Leaf(group)),
398 })));
399 }
400 }
401 } else {
402 groups.push(group);
403 }
404 }
405 _ => {
406 body.push_front(line);
407 let mut rules: Vec<Box<dyn DynRule + 'a>> = vec![];
408 while let Some(next) = body.pop_front() {
409 match next.kind() {
410 Check::Not | Check::Dag | Check::Count(_) => {
411 body.push_front(next);
412 break;
413 }
414 Check::Empty => {
415 rules.push(Box::new(CheckEmpty::new(next.span())));
416 }
417 _ => {
418 rules.push(self.compile_rule(
419 next.ty,
420 next.pattern,
421 config,
422 interner,
423 )?);
424 }
425 }
426 }
427 let group = CheckGroup::Ordered(rules);
428 if let Some((maybe_left, root)) = pending_tree.take() {
429 match maybe_left {
430 Some(left) => {
431 groups.push(CheckGroup::Tree(Box::new(CheckTree::Both {
432 root,
433 left,
434 right: Box::new(CheckTree::Leaf(group)),
435 })));
436 }
437 None => {
438 groups.push(CheckGroup::Tree(Box::new(CheckTree::Right {
439 root,
440 right: Box::new(CheckTree::Leaf(group)),
441 })));
442 }
443 }
444 } else {
445 groups.push(group);
446 }
447 }
448 }
449 }
450 assert!(pending_tree.is_none());
451 if let Some(label) = maybe_label {
452 let label = Pattern::compile_static(label.span, label.pattern, config, interner)?;
453 self.sections.push(CheckSection::Block {
454 label,
455 body: core::mem::take(&mut groups),
456 });
457 } else {
458 for body in core::mem::take(&mut groups).into_iter() {
459 self.sections.push(CheckSection::Group { body });
460 }
461 }
462 }
463
464 Ok(())
465 }
466
467 fn compile_rule(
468 &mut self,
469 ty: CheckType,
470 pattern: CheckPattern<'a>,
471 config: &Config,
472 interner: &mut StringInterner,
473 ) -> DiagResult<Box<dyn DynRule + 'a>> {
474 let pattern = if ty.is_literal_match() {
475 Pattern::compile_literal(pattern, config)?
476 } else {
477 Pattern::compile(pattern, config, interner)?
478 };
479
480 match ty.kind {
481 Check::Plain | Check::Count(_) => {
482 Ok(Box::new(CheckPlain::new(pattern.into_matcher_mut())))
483 }
484 Check::Same => Ok(Box::new(CheckSame::new(pattern.into_matcher_mut()))),
485 Check::Next => Ok(Box::new(CheckNext::new(pattern.into_matcher_mut()))),
486 kind => unreachable!("we should never be compiling a rule for {kind} here"),
487 }
488 }
489}