1use std::collections::HashMap;
4
5use parserc::Span;
6
7use super::ir::*;
8
9const ANALYZER_ERROR: &str = "MLANG_ANALYZER";
10
11#[derive(Debug, thiserror::Error)]
13pub enum AnalyzerError {
14 #[error("duplicate symbol `{0}`, previous declaration is here {1}")]
15 Duplicate(String, Span),
16
17 #[error("Unknown symbol `{0}`.")]
18 Unknown(String),
19
20 #[error("Use group `{0}` as field type declaration, group declaration is here {1}")]
21 Group(String, Span),
22
23 #[error("Unable merge mixin({0})'s fields into node, mixin declaration is here {1}.")]
24 Merge(String, Span),
25
26 #[error("Custom property `{0}`, expect empty call list.")]
27 VariableOption(String),
28
29 #[error("Custom property `rename`, expect one `literial str` as call list.")]
30 Rename,
31}
32
33#[derive(Default)]
34struct SymbolTable(HashMap<String, (Span, usize)>);
35
36impl SymbolTable {
37 fn add(&mut self, index: usize, ident: &Ident) -> bool {
39 if let Some((span, _)) = self.0.insert(ident.1.clone(), (ident.0, index)) {
40 log::error!(target: ANALYZER_ERROR, span:serde; "{}", AnalyzerError::Duplicate(ident.1.clone(), span));
41 false
42 } else {
43 true
44 }
45 }
46
47 fn lookup(&self, ident: &Ident) -> Option<usize> {
49 self.0.get(&ident.1).map(|(_, index)| *index)
50 }
51}
52
53#[derive(Default)]
54struct MixinTable {
55 mixin: HashMap<String, (Span, usize)>,
56}
57
58impl MixinTable {
59 fn add(&mut self, index: usize, ident: &Ident) {
63 self.mixin.insert(ident.1.clone(), (ident.0, index));
64 }
65
66 fn lookup(&self, ident: &Ident) -> Option<usize> {
67 self.mixin.get(ident.1.as_str()).map(|(_, index)| *index)
68 }
69}
70
71#[derive(Default)]
72struct GroupTable {
73 groups: HashMap<String, (Span, usize)>,
74}
75
76impl GroupTable {
77 fn add(&mut self, index: usize, ident: &Ident) {
79 self.groups.insert(ident.1.clone(), (ident.0, index));
80 }
81
82 fn lookup(&self, ident: &Ident) -> Option<usize> {
83 self.groups.get(ident.1.as_str()).map(|(_, index)| *index)
84 }
85}
86
87#[derive(Default)]
89struct SemanticAnalyzer {
90 symbol_table: SymbolTable,
92 merger: MixinTable,
94 digraph_analyzer: GroupTable,
96 errors: usize,
98}
99
100impl SemanticAnalyzer {
101 fn analyze(mut self, opcodes: &mut [Stat]) -> bool {
102 self.build_index(opcodes);
103 self.check(opcodes);
104 self.errors == 0
105 }
106
107 fn build_index(&mut self, opcodes: &mut [Stat]) {
108 for (index, opcode) in opcodes.iter().enumerate() {
109 match opcode {
110 Stat::Element(node) | Stat::Leaf(node) | Stat::Attr(node) | Stat::Data(node) => {
111 if !self.symbol_table.add(index, &node.ident) {
112 self.errors += 1;
113 }
114 }
115 Stat::Mixin(node) => {
116 if !self.symbol_table.add(index, &node.ident) {
117 self.errors += 1;
118 }
119 self.merger.add(index, &node.ident);
120 }
121 Stat::Enum(node) => {
122 if !self.symbol_table.add(index, &node.ident) {
123 self.errors += 1;
124 }
125 }
126 Stat::Group(node) => {
127 if !self.symbol_table.add(index, &node.ident) {
128 self.errors += 1;
129 }
130 self.digraph_analyzer.add(index, &node.ident);
131 }
132 Stat::ApplyTo(_) => {}
133 Stat::ChildrenOf(_) => {}
134 }
135 }
136 }
137
138 fn check(&mut self, opcodes: &mut [Stat]) {
139 let mut updates = vec![];
140 for (index, opcode) in opcodes.iter().enumerate() {
141 match opcode {
142 Stat::Element(node) => {
143 if let Some(node) = self.node_check(opcodes, node) {
144 updates.push((index, Stat::Element(Box::new(node))));
145 }
146 }
147 Stat::Leaf(node) => {
148 if let Some(node) = self.node_check(opcodes, node) {
149 updates.push((index, Stat::Leaf(Box::new(node))));
150 }
151 }
152 Stat::Attr(node) => {
153 if let Some(node) = self.node_check(opcodes, node) {
154 updates.push((index, Stat::Attr(Box::new(node))));
155 }
156 }
157 Stat::Mixin(node) => {
158 assert_eq!(
159 self.node_check(opcodes, node),
160 None,
161 "Mixin: inner error, mixin can't mixin other one."
162 );
163 }
164 Stat::Data(node) => {
165 if let Some(node) = self.node_check(opcodes, node) {
166 updates.push((index, Stat::Data(Box::new(node))));
167 }
168 }
169 Stat::Enum(node) => {
170 self.enum_check(opcodes, node);
171 }
172 Stat::Group(group) => {
173 self.group_check(opcodes, group);
174 }
175 Stat::ApplyTo(apply_to) => {
176 if let Some(opcode) = self.apply_to_check(opcodes, apply_to) {
177 updates.push((index, opcode));
178 }
179 }
180 Stat::ChildrenOf(children_of) => {
181 if let Some(opcode) = self.children_of_check(opcodes, children_of) {
182 updates.push((index, opcode));
183 }
184 }
185 }
186 }
187
188 for (index, update) in updates {
189 opcodes[index] = update;
190 }
191 }
192
193 fn symbol_check(&mut self, opcodes: &[Stat], ident: &Ident, expect_type: bool) -> bool {
194 if let Some(index) = self.symbol_table.lookup(ident) {
195 if let Stat::Group(group) = &opcodes[index] {
196 if expect_type {
197 self.errors += 1;
198 log::error!(
199 target: ANALYZER_ERROR, span:serde = ident.0;
200 "{}", AnalyzerError::Group(group.ident.1.clone(), group.ident.0)
201 );
202 }
203
204 return false;
205 }
206
207 return true;
208 } else {
209 self.errors += 1;
210 log::error!(
211 target: ANALYZER_ERROR, span:serde = ident.0;
212 "{}", AnalyzerError::Unknown(ident.1.clone())
213 );
214 return false;
215 }
216 }
217
218 fn type_check(&mut self, opcodes: &[Stat], ty: &Type) {
219 match ty {
220 Type::Data(ident) => {
221 self.symbol_check(opcodes, ident, true);
222 }
223
224 Type::ListOf(component, _) => {
225 self.type_check(opcodes, component);
226 }
227 Type::ArrayOf(component, _, _) => {
228 self.type_check(opcodes, component);
229 }
230 _ => {}
231 }
232 }
233
234 fn node_check(&mut self, opcodes: &[Stat], node: &Node) -> Option<Node> {
235 for field in node.fields.iter() {
236 self.type_check(opcodes, &field.ty());
237 }
238
239 for property in &node.properties {
240 for call in &property.calls {
241 match call.target.1.as_str() {
242 "option" | "variable" | "init" => {
243 if call.params.len() != 0 {
244 self.errors += 1;
245 log::error!(
246 target: ANALYZER_ERROR, span:serde = call.target.0;
247 "{}", AnalyzerError::VariableOption(call.target.1.clone())
248 );
249 }
250 }
251 "rename" => {
252 if call.params.len() != 1 {
253 log::error!(
254 target: ANALYZER_ERROR,
255 span:serde = call.target.0; "{}", AnalyzerError::Rename
256 );
257 }
258 }
259 _ => {}
260 }
261 }
262 }
263
264 if let Some(mixin) = &node.mixin {
265 if let Some(index) = self.merger.lookup(mixin) {
266 if let Stat::Mixin(mixin) = &opcodes[index] {
267 let expand = mixin.fields.clone();
268 let fields = node.fields.clone();
269
270 let fields = match fields.append(expand) {
271 Ok(fields) => fields,
272 Err(fields) => {
273 log::error!(
274 target: ANALYZER_ERROR,
275 span:serde = node.ident.0;
276 "{}",
277 AnalyzerError::Merge(mixin.ident.1.clone(), mixin.ident.0)
278 );
279 fields
280 }
281 };
282
283 return Some(Node {
284 span: node.span,
285 comments: node.comments.clone(),
286 mixin: None,
287 properties: node.properties.clone(),
288 ident: node.ident.clone(),
289 fields,
290 });
291 } else {
292 panic!("node_check(mxin): inner error.");
293 }
294 } else {
295 log::error!(
296 target: ANALYZER_ERROR,
297 span:serde = mixin.0; "{}", AnalyzerError::Unknown(mixin.1.clone())
298 );
299 return None;
300 }
301 }
302
303 return None;
304 }
305
306 fn enum_check(&mut self, opcodes: &[Stat], node: &Enum) {
307 for field_node in &node.fields {
308 for field in field_node.fields.iter() {
309 self.type_check(opcodes, field.ty());
310 }
311 }
312 }
313
314 fn group_check(&mut self, opcodes: &[Stat], node: &Group) {
315 for ident in &node.children {
316 self.symbol_check(opcodes, ident, true);
317 }
318 }
319
320 fn expand_with_group(&self, opcodes: &[Stat], ident: &Ident) -> Option<Vec<Ident>> {
321 if let Some(index) = self.digraph_analyzer.lookup(ident) {
322 if let Stat::Group(group) = &opcodes[index] {
323 return Some(group.children.clone());
324 } else {
325 panic!("expand_with_group: inner error.");
326 }
327 } else {
328 log::error!(
329 target: ANALYZER_ERROR,
330 span:serde = ident.0; "{}", AnalyzerError::Unknown(ident.1.clone())
331 );
332 None
333 }
334 }
335
336 fn apply_to_check(&mut self, opcodes: &[Stat], node: &ApplyTo) -> Option<Stat> {
337 let mut from_expand = vec![];
338
339 for ident in &node.from {
340 if !self.symbol_check(opcodes, ident, false) {
341 if let Some(mut expand) = self.expand_with_group(opcodes, ident) {
342 from_expand.append(&mut expand);
343 }
344 } else {
345 from_expand.push(ident.clone());
346 }
347 }
348
349 let mut to_expand = vec![];
350
351 for ident in &node.to {
352 if !self.symbol_check(opcodes, ident, false) {
353 if let Some(mut expand) = self.expand_with_group(opcodes, ident) {
354 to_expand.append(&mut expand);
355 }
356 } else {
357 to_expand.push(ident.clone());
358 }
359 }
360
361 Some(Stat::ApplyTo(Box::new(ApplyTo {
362 from: from_expand,
363 to: to_expand,
364 span: node.span,
365 comments: node.comments.clone(),
366 properties: node.properties.clone(),
367 })))
368 }
369
370 fn children_of_check(&mut self, opcodes: &[Stat], node: &ChildrenOf) -> Option<Stat> {
371 let mut from_expand = vec![];
372
373 for ident in &node.from {
374 if !self.symbol_check(opcodes, ident, false) {
375 if let Some(mut expand) = self.expand_with_group(opcodes, ident) {
376 from_expand.append(&mut expand);
377 }
378 } else {
379 from_expand.push(ident.clone());
380 }
381 }
382
383 let mut to_expand = vec![];
384
385 for ident in &node.to {
386 if !self.symbol_check(opcodes, ident, false) {
387 if let Some(mut expand) = self.expand_with_group(opcodes, ident) {
388 to_expand.append(&mut expand);
389 }
390 } else {
391 to_expand.push(ident.clone());
392 }
393 }
394
395 Some(Stat::ChildrenOf(Box::new(ChildrenOf {
396 from: from_expand,
397 to: to_expand,
398 span: node.span,
399 comments: node.comments.clone(),
400 properties: node.properties.clone(),
401 })))
402 }
403}
404
405pub fn semantic_analyze(opcodes: &mut [Stat]) -> bool {
407 SemanticAnalyzer::default().analyze(opcodes)
408}