1use crate::dckeyword::DCKeyword;
24use crate::dclass::DClass;
25use crate::dcstruct::DCStruct;
26use crate::dctype::DCTypeDefinition;
27use crate::globals;
28use crate::hashgen::*;
29use crate::parser::ast;
30
31#[derive(Debug, Clone)]
33pub struct DCPythonImport {
34 pub module: String,
35 pub symbols: Vec<String>,
36}
37
38impl std::fmt::Display for DCPythonImport {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 if self.symbols.is_empty() {
41 write!(f, "import ")?;
42 f.write_str(&self.module)?;
43 } else {
44 write!(f, "from ")?;
45 f.write_str(&self.module)?;
46
47 write!(f, " import ")?;
48 for (i, symbol) in self.symbols.iter().enumerate() {
49 f.write_str(symbol)?;
50
51 if i != self.symbols.len() - 1 {
52 write!(f, ", ")?;
53 }
54 }
55 }
56 Ok(())
57 }
58}
59
60#[derive(Debug)]
64pub struct DCFile {
65 pub(crate) imports: Vec<DCPythonImport>,
66 pub(crate) type_defs: Vec<DCTypeDefinition>,
67 pub(crate) keywords: Vec<DCKeyword>,
68 pub(crate) structs: Vec<DCStruct>,
69 pub(crate) dclasses: Vec<DClass>,
70 pub(crate) all_object_valid: bool,
71 pub(crate) inherited_fields_stale: bool,
72}
73
74impl std::fmt::Display for DCFile {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 if !self.imports.is_empty() {
78 for import in &self.imports {
79 import.fmt(f)?;
80 writeln!(f)?;
81 }
82 writeln!(f)?;
83 }
84 for type_def in &self.type_defs {
86 type_def.fmt(f)?;
87 writeln!(f)?;
88 }
89 if !self.keywords.is_empty() {
90 writeln!(f)?;
91 }
92 for kw in &self.keywords {
94 kw.fmt(f)?;
95 writeln!(f)?;
96 }
97 if !self.structs.is_empty() {
98 writeln!(f)?;
99 }
100 for strukt in &self.structs {
102 strukt.fmt(f)?;
103 writeln!(f)?;
104 }
105 if !self.dclasses.is_empty() {
106 writeln!(f)?;
107 }
108 for dclass in &self.dclasses {
110 dclass.fmt(f)?;
111 writeln!(f)?;
112 }
113 Ok(())
114 }
115}
116
117impl LegacyDCHash for DCFile {
118 fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
119 hashgen.add_int(self.get_num_dclasses().try_into().unwrap());
120
121 for dclass in &self.dclasses {
122 dclass.generate_hash(hashgen);
123 }
124
125 hashgen.add_int(self.get_num_structs().try_into().unwrap());
126
127 for strukt in &self.structs {
128 strukt.generate_hash(hashgen);
129 }
130
131 hashgen.add_int(self.get_num_keywords().try_into().unwrap());
132
133 for kw in &self.keywords {
134 kw.generate_hash(hashgen);
135 }
136 }
137}
138
139impl DCFile {
140 pub fn get_legacy_hash(&self) -> globals::DCFileHash {
144 let mut hashgen: DCHashGenerator = DCHashGenerator::default();
145
146 self.generate_hash(&mut hashgen);
147 hashgen.get_hash()
148 }
149
150 pub fn get_pretty_hash(&self) -> String {
152 format!("0x{:0width$x}", self.get_legacy_hash(), width = 8) }
154
155 pub fn get_num_imports(&self) -> usize {
158 self.imports.len()
159 }
160
161 pub fn get_python_import(&self, index: usize) -> &DCPythonImport {
162 self.imports.get(index).expect("Index out of bounds.")
163 }
164
165 pub fn get_num_keywords(&self) -> usize {
168 self.keywords.len()
169 }
170
171 pub fn get_keyword(&self, _index: usize) -> &DCKeyword {
172 todo!();
173 }
174
175 pub fn has_keyword(&self, _keyword: String) -> bool {
176 todo!();
177 }
178
179 pub fn get_num_dclasses(&self) -> usize {
182 self.dclasses.len()
183 }
184
185 pub fn get_dclass(&self, _index: usize) -> &DClass {
186 todo!();
187 }
188
189 pub fn get_dclass_by_id(&self, id: globals::DClassId) -> &DClass {
190 self.dclasses.get(usize::from(id)).unwrap()
191 }
192
193 pub fn get_dclass_by_name(&self, _name: &str) -> &DClass {
194 todo!();
195 }
196
197 pub fn get_num_structs(&self) -> usize {
200 self.structs.len()
201 }
202
203 pub fn get_struct(&self, _index: usize) -> &DCStruct {
204 todo!();
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn write_dc_python_import() {
214 let import: DCPythonImport = DCPythonImport {
215 module: "views".to_string(),
216 symbols: vec![],
217 };
218
219 assert_eq!(import.to_string(), "import views");
220 }
221
222 #[test]
223 fn write_dcfile_py_imports() {
224 let imports: Vec<DCPythonImport> = vec![
225 DCPythonImport {
226 module: "views".to_string(),
227 symbols: vec![],
228 },
229 DCPythonImport {
230 module: "views".to_string(),
231 symbols: vec!["DistributedDonut".to_string()],
232 },
233 DCPythonImport {
234 module: "views".to_string(),
235 symbols: vec!["Class".to_string(), "ClassAI".to_string(), "ClassOV".to_string()],
236 },
237 ];
238
239 let dcf: DCFile = DCFile {
240 structs: vec![],
241 dclasses: vec![],
242 imports,
243 keywords: vec![],
244 type_defs: vec![],
245 all_object_valid: false,
246 inherited_fields_stale: false,
247 };
248
249 assert_eq!(
250 dcf.to_string(),
251 "\
252 import views\n\
253 from views import DistributedDonut\n\
254 from views import Class, ClassAI, ClassOV\n\
255 \n\
256 ",
257 );
258 }
259}
260
261pub(crate) mod semantics {
262 use super::ast;
263 use crate::parser::error::{Diagnostic, SemanticError};
264 use crate::parser::pipeline::{PipelineData, TopLevelSymbol};
265 use std::collections::HashSet;
266
267 fn analyze_view_suffixes(pipeline: &mut PipelineData, view_suffixes: &ast::ViewSuffixes) {
272 let mut recorded_suffixes: HashSet<String> = HashSet::default();
273
274 for view_suffix in view_suffixes {
275 if !recorded_suffixes.insert(view_suffix.view.clone()) {
276 let diag: Diagnostic = Diagnostic::error(
277 view_suffix.span,
278 pipeline,
279 SemanticError::RedundantViewSuffix(view_suffix.view.clone()),
280 );
281
282 pipeline
283 .emit_diagnostic(diag.into())
284 .expect("Failed to emit diagnostic.");
285 }
286 }
287 }
288
289 pub fn analyze_python_import(pipeline: &mut PipelineData, import: &ast::PythonImport) {
290 analyze_view_suffixes(pipeline, &import.module.symbol_views);
292 analyze_view_suffixes(pipeline, &import.class.symbol_views);
293 }
294
295 pub fn analyze_keyword(pipeline: &mut PipelineData, keyword: &ast::KeywordDefinition) {
296 let already_defined: bool = pipeline
297 .dc_data
298 .symbol_exists(&keyword.identifier, TopLevelSymbol::KeywordDef);
299
300 if already_defined {
301 let diag: Diagnostic = Diagnostic::error(
302 keyword.span,
303 pipeline,
304 SemanticError::AlreadyDefined(keyword.identifier.clone()),
305 );
306
307 pipeline
308 .emit_diagnostic(diag.into())
309 .expect("Failed to emit diagnostic.");
310 return;
311 }
312 pipeline
314 .dc_data
315 .register_symbol(keyword.identifier.clone(), TopLevelSymbol::KeywordDef);
316 }
317
318 pub fn analyze_typedef(pipeline: &mut PipelineData, typedef: &ast::TypeDefinition) {
319 }
323
324 pub fn analyze_dclass(pipeline: &mut PipelineData, dclass: &ast::DClass) {
325 }
327
328 pub fn analyze_struct(pipeline: &mut PipelineData, strukt: &ast::Struct) {
329 }
332}
333
334pub(crate) mod generation {
335 use super::DCPythonImport;
336 use crate::parser::ast;
337
338 pub fn add_python_import(import: &ast::PythonImport) -> Vec<DCPythonImport> {
343 let mut imports: Vec<DCPythonImport> = vec![];
344 let mut class_symbols: Vec<String> = vec![import.class.symbol.clone()];
345
346 if !import.class.symbol_views.is_empty() {
348 for class_suffix in &import.class.symbol_views {
349 class_symbols.push(import.class.symbol.clone() + &class_suffix.view);
350 }
351 }
352
353 if !import.module.symbol_views.is_empty() {
355 let mut c_symbol: String = class_symbols.first().unwrap().clone();
356
357 imports.push(DCPythonImport {
358 module: import.module.symbol.clone(),
359 symbols: vec![c_symbol],
360 });
361
362 for (i, module_suffix) in import.module.symbol_views.iter().enumerate() {
363 let full_import: String = import.module.symbol.clone() + &module_suffix.view;
364
365 if (class_symbols.len() - 1) <= i {
366 c_symbol = class_symbols.last().unwrap().clone();
367 } else {
368 c_symbol = class_symbols.get(i + 1).unwrap().clone();
369 }
370
371 imports.push(DCPythonImport {
372 module: full_import,
373 symbols: vec![c_symbol],
374 });
375 }
376 } else {
377 imports.push(DCPythonImport {
379 module: import.module.symbol.clone(),
380 symbols: class_symbols,
381 });
382 }
383
384 imports
385 }
386}