Skip to main content

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}