plotnik_compiler/analyze/
symbol_table.rs1use indexmap::IndexMap;
8
9use crate::Diagnostics;
10use crate::diagnostics::DiagnosticKind;
11use crate::parser::{Root, ast, token_src};
12
13use super::visitor::Visitor;
14use crate::query::{SourceId, SourceMap};
15
16pub const UNNAMED_DEF: &str = "_";
19
20#[derive(Clone, Debug, Default)]
25pub struct SymbolTable {
26 table: IndexMap<String, ast::Expr>,
28 files: IndexMap<String, SourceId>,
30}
31
32impl SymbolTable {
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn insert(&mut self, name: &str, source_id: SourceId, expr: ast::Expr) -> bool {
42 let is_new = !self.table.contains_key(name);
43 self.table.insert(name.to_owned(), expr);
44 self.files.insert(name.to_owned(), source_id);
45 is_new
46 }
47
48 pub fn remove(&mut self, name: &str) -> Option<(SourceId, ast::Expr)> {
50 let expr = self.table.shift_remove(name)?;
51 let source_id = self.files.shift_remove(name)?;
52 Some((source_id, expr))
53 }
54
55 pub fn contains(&self, name: &str) -> bool {
57 self.table.contains_key(name)
58 }
59
60 pub fn get(&self, name: &str) -> Option<&ast::Expr> {
62 self.table.get(name)
63 }
64
65 pub fn source_id(&self, name: &str) -> Option<SourceId> {
67 self.files.get(name).copied()
68 }
69
70 pub fn get_full(&self, name: &str) -> Option<(SourceId, &ast::Expr)> {
72 let expr = self.table.get(name)?;
73 let source_id = self.files.get(name).copied()?;
74 Some((source_id, expr))
75 }
76
77 pub fn len(&self) -> usize {
79 self.table.len()
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.table.is_empty()
85 }
86
87 pub fn keys(&self) -> impl Iterator<Item = &str> {
89 self.table.keys().map(String::as_str)
90 }
91
92 pub fn iter(&self) -> impl Iterator<Item = (&str, &ast::Expr)> {
94 self.table.iter().map(|(k, v)| (k.as_str(), v))
95 }
96
97 pub fn iter_full(&self) -> impl Iterator<Item = (&str, SourceId, &ast::Expr)> {
99 self.table.iter().map(|(k, v)| {
100 let source_id = self.files[k];
101 (k.as_str(), source_id, v)
102 })
103 }
104}
105
106pub fn resolve_names(
107 source_map: &SourceMap,
108 ast_map: &IndexMap<SourceId, Root>,
109 diag: &mut Diagnostics,
110) -> SymbolTable {
111 let mut symbol_table = SymbolTable::new();
112
113 for (&source_id, ast) in ast_map {
115 let src = source_map.content(source_id);
116 let mut resolver = ReferenceResolver {
117 src,
118 source_id,
119 diag,
120 symbol_table: &mut symbol_table,
121 };
122 resolver.visit(ast);
123 }
124
125 for (&source_id, ast) in ast_map {
127 let mut validator = ReferenceValidator {
128 source_id,
129 diag,
130 symbol_table: &symbol_table,
131 };
132 validator.visit(ast);
133 }
134
135 symbol_table
136}
137
138struct ReferenceResolver<'q, 'd, 'a> {
139 src: &'q str,
140 source_id: SourceId,
141 diag: &'d mut Diagnostics,
142 symbol_table: &'a mut SymbolTable,
143}
144
145impl Visitor for ReferenceResolver<'_, '_, '_> {
146 fn visit_def(&mut self, def: &ast::Def) {
147 let Some(body) = def.body() else { return };
148
149 if let Some(token) = def.name() {
150 let name = token_src(&token, self.src);
152 if self.symbol_table.contains(name) {
153 self.diag
154 .report(
155 self.source_id,
156 DiagnosticKind::DuplicateDefinition,
157 token.text_range(),
158 )
159 .message(name)
160 .emit();
161 } else {
162 self.symbol_table.insert(name, self.source_id, body);
163 }
164 } else {
165 if self.symbol_table.contains(UNNAMED_DEF) {
168 self.symbol_table.remove(UNNAMED_DEF);
169 }
170 self.symbol_table.insert(UNNAMED_DEF, self.source_id, body);
171 }
172 }
173}
174
175struct ReferenceValidator<'d, 'a> {
176 source_id: SourceId,
177 diag: &'d mut Diagnostics,
178 symbol_table: &'a SymbolTable,
179}
180
181impl Visitor for ReferenceValidator<'_, '_> {
182 fn visit_ref(&mut self, r: &ast::Ref) {
183 let Some(name_token) = r.name() else { return };
184 let name = name_token.text();
185
186 if self.symbol_table.contains(name) {
187 return;
188 }
189
190 self.diag
191 .report(
192 self.source_id,
193 DiagnosticKind::UndefinedReference,
194 name_token.text_range(),
195 )
196 .message(name)
197 .emit();
198 }
199}