diff options
Diffstat (limited to 'src/eval/analysis.rs')
-rw-r--r-- | src/eval/analysis.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/eval/analysis.rs b/src/eval/analysis.rs new file mode 100644 index 0000000..b3a0083 --- /dev/null +++ b/src/eval/analysis.rs | |||
@@ -0,0 +1,108 @@ | |||
1 | use crate::{ast, Wrap}; | ||
2 | |||
3 | #[derive(Debug, PartialEq, Eq)] | ||
4 | pub struct Analysis { | ||
5 | diagnostics: Vec<Diagnostic>, | ||
6 | } | ||
7 | |||
8 | impl Analysis { | ||
9 | pub fn print(&self) { | ||
10 | for Diagnostic { level, kind } in &self.diagnostics { | ||
11 | eprintln!("{level} {kind}"); | ||
12 | } | ||
13 | } | ||
14 | } | ||
15 | |||
16 | #[derive(Debug, PartialEq, Eq)] | ||
17 | pub struct Diagnostic { | ||
18 | level: Level, | ||
19 | kind: Kind, | ||
20 | } | ||
21 | |||
22 | #[derive(Debug, PartialEq, Eq)] | ||
23 | pub enum Level { | ||
24 | Warning, | ||
25 | Error, | ||
26 | } | ||
27 | |||
28 | impl std::fmt::Display for Level { | ||
29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
30 | match self { | ||
31 | Level::Warning => write!(f, "[warning]"), | ||
32 | Level::Error => write!(f, "[error]"), | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | |||
37 | #[derive(Debug, PartialEq, Eq)] | ||
38 | pub enum Kind { | ||
39 | Pattern { | ||
40 | invalid_node_kind: String, | ||
41 | full_pattern: ast::Pattern, | ||
42 | }, | ||
43 | } | ||
44 | |||
45 | impl std::fmt::Display for Kind { | ||
46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
47 | match self { | ||
48 | Self::Pattern { | ||
49 | invalid_node_kind, | ||
50 | full_pattern, | ||
51 | } => { | ||
52 | write!( | ||
53 | f, | ||
54 | "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`" | ||
55 | ) | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub fn run(ctx: &super::Context) -> Analysis { | ||
62 | [validate_patterns(ctx)] | ||
63 | .into_iter() | ||
64 | .flatten() | ||
65 | .collect::<Vec<_>>() | ||
66 | .wrap(|diagnostics| Analysis { diagnostics }) | ||
67 | } | ||
68 | |||
69 | fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> { | ||
70 | fn validate_pattern( | ||
71 | pattern: &ast::TreePattern, | ||
72 | language: &tree_sitter::Language, | ||
73 | ) -> Option<String> { | ||
74 | match pattern { | ||
75 | ast::TreePattern::Atom(a) => { | ||
76 | if language.id_for_node_kind(a, true) == 0 { | ||
77 | Some(a.to_owned()) | ||
78 | } else { | ||
79 | None | ||
80 | } | ||
81 | } | ||
82 | ast::TreePattern::List(items) => { | ||
83 | for item in items { | ||
84 | validate_pattern(item, language)?; | ||
85 | } | ||
86 | None | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | ctx.program | ||
92 | .stanzas | ||
93 | .iter() | ||
94 | .flat_map(|s| match &s.pattern { | ||
95 | ast::Pattern::Begin | ast::Pattern::End => None, | ||
96 | ast::Pattern::Tree { matcher, .. } => { | ||
97 | validate_pattern(matcher, &ctx.language).map(|invalid_node_kind| Kind::Pattern { | ||
98 | invalid_node_kind, | ||
99 | full_pattern: s.pattern.clone(), | ||
100 | }) | ||
101 | } | ||
102 | }) | ||
103 | .map(|kind| Diagnostic { | ||
104 | level: Level::Error, | ||
105 | kind, | ||
106 | }) | ||
107 | .collect() | ||
108 | } | ||