plotnik_lib/query/
symbol_table.rs1use indexmap::IndexMap;
8
9pub const UNNAMED_DEF: &str = "_";
12
13use crate::diagnostics::DiagnosticKind;
14use crate::parser::{Expr, Ref, ast, token_src};
15
16use super::Query;
17
18pub type SymbolTable<'src> = IndexMap<&'src str, ast::Expr>;
19
20impl<'a> Query<'a> {
21 pub(super) fn resolve_names(&mut self) {
22 for def in self.ast.defs() {
24 let (name, is_named) = match def.name() {
25 Some(token) => (token_src(&token, self.source), true),
26 None => (UNNAMED_DEF, false),
27 };
28
29 if is_named && self.symbol_table.contains_key(name) {
31 let name_token = def.name().unwrap();
32 self.resolve_diagnostics
33 .report(DiagnosticKind::DuplicateDefinition, name_token.text_range())
34 .message(name)
35 .emit();
36 continue;
37 }
38
39 if !is_named && self.symbol_table.contains_key(name) {
41 self.symbol_table.shift_remove(name);
42 }
43
44 let Some(body) = def.body() else {
45 continue;
46 };
47 self.symbol_table.insert(name, body);
48 }
49
50 let defs: Vec<_> = self.ast.defs().collect();
52 for def in defs {
53 let Some(body) = def.body() else { continue };
54 self.collect_reference_diagnostics(&body);
55 }
56
57 assert!(
59 self.ast.exprs().next().is_none(),
60 "symbol_table: unexpected bare Expr in Root (parser should wrap in Def)"
61 );
62 }
63
64 fn collect_reference_diagnostics(&mut self, expr: &Expr) {
65 if let Expr::Ref(r) = expr {
66 self.check_ref_diagnostic(r);
67 }
68
69 for child in expr.children() {
70 self.collect_reference_diagnostics(&child);
71 }
72 }
73
74 fn check_ref_diagnostic(&mut self, r: &Ref) {
75 let Some(name_token) = r.name() else { return };
76 let name = name_token.text();
77
78 if self.symbol_table.contains_key(name) {
79 return;
80 }
81
82 self.resolve_diagnostics
83 .report(DiagnosticKind::UndefinedReference, name_token.text_range())
84 .message(name)
85 .emit();
86 }
87}