tramlines_core/grammar/lower/checks.rs
1//! The checks of a rule the generic check cannot see that every format
2//! shares: the example a shape shows, and guidance that is not the example.
3
4use super::{Lowerer, list_of, str_of};
5use crate::Spanned;
6
7impl Lowerer<'_> {
8 /// A rule that declares a shape shows an example of it. A regex is a
9 /// shape and not an instruction, and the example is the one voice a rule
10 /// has: every failure it raises shows it, and the scaffold
11 /// writes it. A unit rule bound to a format takes the format's, where
12 /// `format_speaks`; the `format` of a frontmatter value checks the value
13 /// and has no example. A rule that only selects, a count and a `text`
14 /// state nothing.
15 // sokf-implements: TL_66
16 // sokf-implements: TL_67
17 pub fn explained(&mut self, node: &Spanned, shapes: &[&str], format_speaks: bool) {
18 if node.get("example").is_some()
19 || (format_speaks && node.get("format").is_some())
20 || node.get("text").is_some()
21 {
22 return;
23 }
24 let Some(key) = shapes.iter().find(|k| node.get(k).is_some()) else {
25 return;
26 };
27 self.fail(
28 node.line,
29 "TL_66",
30 format!(
31 "a rule that declares `{key}` carries an `example` of it: a failure shows the example, and the scaffold writes it"
32 ),
33 );
34 }
35
36 /// Guidance says what the example cannot, so the two holding one
37 /// sentence, or guidance that is one text of an `example` list, is
38 /// guidance that says nothing.
39 // sokf-implements: TL_68
40 pub fn one_voice(&mut self, node: &Spanned) {
41 if let Some(guidance) = str_of(node, "guidance") {
42 self.one_voice_in(node.line, &guidance, list_of(node, "example"));
43 }
44 }
45
46 /// As `one_voice`, for `guidance` and each of `examples`, which the
47 /// format gathers from wherever its scaffold writes them after it.
48 pub fn one_voice_in(
49 &mut self,
50 line: usize,
51 guidance: &str,
52 examples: impl IntoIterator<Item = String>,
53 ) {
54 if examples.into_iter().any(|e| guidance.trim() == e.trim()) {
55 self.fail(
56 line,
57 "TL_68",
58 "`guidance` and `example` hold the same words: guidance says what the example cannot",
59 );
60 }
61 }
62
63 /// A rule's `min` is at most its `max`.
64 // sokf-implements: TL_98
65 pub fn check_bounds(&mut self, line: usize, min: usize, max: Option<usize>) {
66 if let Some(max) = max
67 && min > max
68 {
69 self.fail(line, "TL_98", format!("`min` {min} is above `max` {max}"));
70 }
71 }
72}