donet_core/parser/semantics.rs
1/*
2 This file is part of Donet.
3
4 Copyright © 2024-2025 Max Rodriguez
5
6 Donet is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Affero General Public License,
8 as published by the Free Software Foundation, either version 3
9 of the License, or (at your option) any later version.
10
11 Donet is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with Donet. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20//! The DC parser outputs an [`Abstract Syntax Tree`], which is just a big
21//! nested structure that defines the declarations in the DC file. At runtime,
22//! the Donet daemon (and its services) need a class hierarchy structure in
23//! memory to access while processing network messages.
24//!
25//! This source file defines the process of taking in the DC file abstract
26//! syntax tree as input and generating an output of a class hierarchy structure,
27//! where each class has methods that make it easy for the Donet daemon to look up
28//! information on the DC contract at runtime in order to understand the
29//! network messages it receives.
30//!
31//! [`Abstract Syntax Tree`]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
32
33use super::ast;
34use super::error::DCReadError;
35use super::PipelineData;
36use crate::dcfile;
37use anyhow::Result;
38
39/// Takes in the [`Abstract Syntax Trees`] from the last stage of the pipeline
40/// and outputs error diagnostics if any issues are found.
41///
42/// [`Abstract Syntax Trees`]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
43///
44pub fn semantic_analyzer(pipeline: &mut PipelineData) -> Result<(), DCReadError> {
45 // tell the pipeline we are moving onto the next stage
46 pipeline.next_stage();
47
48 // Iterate through all ASTs and analyze it.
49 for ast in pipeline.syntax_trees.clone() {
50 for type_declaration in ast.type_declarations.iter() {
51 match type_declaration {
52 ast::TypeDeclaration::PythonImport(import) => {
53 dcfile::semantics::analyze_python_import(pipeline, import);
54 }
55 ast::TypeDeclaration::KeywordType(keyword) => {
56 dcfile::semantics::analyze_keyword(pipeline, keyword);
57 }
58 ast::TypeDeclaration::StructType(_strukt) => {
59 // TODO
60 }
61 ast::TypeDeclaration::DClassType(_dclass) => {
62 // TODO
63 }
64 ast::TypeDeclaration::TypedefType(_typedef) => {
65 // TODO
66 }
67 }
68 }
69 pipeline.next_file(); // tell the pipeline we are processing the next file
70 }
71
72 if pipeline.failing() {
73 Err(DCReadError::Semantic)
74 } else {
75 Ok(())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::read_dc;
83 use dcfile::DCPythonImport;
84
85 #[test]
86 fn python_imports() {
87 let dc_string: &str = "
88 from views import *
89 from views import DistributedDonut
90 from views import Class/AI/OV
91 ";
92
93 let dcf: dcfile::DCFile = read_dc(dc_string.into()).expect("Failed to parse syntax.");
94
95 let num_imports: usize = dcf.get_num_imports();
96 assert_eq!(num_imports, 3);
97
98 let symbols: Vec<Vec<String>> = vec![
99 vec!["*".into()],
100 vec!["DistributedDonut".into()],
101 vec!["Class".into(), "ClassAI".into(), "ClassOV".into()],
102 ];
103
104 for index in 0..num_imports - 1 {
105 let import: &DCPythonImport = dcf.get_python_import(index);
106
107 assert_eq!(import.module, "views");
108
109 let target_symbols: &Vec<String> = symbols.get(index).unwrap();
110
111 assert_eq!(*target_symbols, import.symbols);
112 }
113 }
114
115 #[test]
116 #[should_panic]
117 fn redundant_view_suffix() {
118 let dc_string: &str = "
119 from views import Class/AI/OV/OV
120 ";
121
122 let _ = read_dc(dc_string.into()).expect("Should fail.");
123 }
124
125 #[test]
126 #[should_panic]
127 fn keyword_already_defined() {
128 let dc_string: &str = "
129 keyword abcdef;
130 keyword abcdef;
131 ";
132
133 let _ = read_dc(dc_string.into()).expect("Should fail.");
134 }
135}