1use crate::dcfield::DCField;
24use crate::dckeyword::DCKeyword;
25use crate::dclass::DClass;
26use crate::dconfig::*;
27use crate::dcstruct::DCStruct;
28use crate::dctype::DCTypeDefinition;
29use crate::globals;
30use crate::hashgen::*;
31use crate::parser::ast;
32
33#[derive(Debug, Clone)]
35pub struct DCPythonImport {
36 pub module: String,
37 pub symbols: Vec<String>,
38}
39
40impl From<interim::PythonImport> for DCPythonImport {
41 fn from(value: interim::PythonImport) -> Self {
42 Self {
43 module: value.module,
44 symbols: value.symbols,
45 }
46 }
47}
48
49impl std::fmt::Display for DCPythonImport {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 if self.symbols.is_empty() {
52 write!(f, "import ")?;
53 f.write_str(&self.module)?;
54 } else {
55 write!(f, "from ")?;
56 f.write_str(&self.module)?;
57
58 write!(f, " import ")?;
59 for (i, symbol) in self.symbols.iter().enumerate() {
60 f.write_str(symbol)?;
61
62 if i != self.symbols.len() - 1 {
63 write!(f, ", ")?;
64 }
65 }
66 }
67 Ok(())
68 }
69}
70
71#[derive(Debug, Clone)]
75pub struct DCFile<'dc> {
76 config: DCFileConfig,
77 baked_legacy_hash: globals::DCFileHash,
78 structs: Vec<DCStruct<'dc>>,
79 dclasses: Vec<DClass<'dc>>,
80 imports: Vec<DCPythonImport>,
81 keywords: Vec<DCKeyword>,
82 type_defs: Vec<DCTypeDefinition>,
83 field_id_2_field: Vec<&'dc DCField<'dc>>,
84 all_object_valid: bool,
86 inherited_fields_stale: bool,
87}
88
89impl From<interim::DCFile> for DCFile<'_> {
90 fn from(value: interim::DCFile) -> Self {
91 let mut imports: Vec<DCPythonImport> = vec![];
92 let mut keywords: Vec<DCKeyword> = vec![];
93
94 for imp in value.imports {
95 imports.push(imp.into());
96 }
97
98 for kw in value.keywords {
99 keywords.push(kw.into());
100 }
101
102 Self {
103 config: value.config,
104 baked_legacy_hash: 0_u32,
105 structs: vec![],
106 dclasses: vec![],
107 imports,
108 keywords,
109 type_defs: vec![],
110 field_id_2_field: vec![],
111 all_object_valid: true,
112 inherited_fields_stale: false,
113 }
114 }
115}
116
117impl std::fmt::Display for DCFile<'_> {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.write_str(&self.config.to_string())?;
121
122 if !self.imports.is_empty() {
124 for import in &self.imports {
125 import.fmt(f)?;
126 writeln!(f)?;
127 }
128 writeln!(f)?;
129 }
130 for type_def in &self.type_defs {
132 type_def.fmt(f)?;
133 writeln!(f)?;
134 }
135 for kw in &self.keywords {
137 kw.fmt(f)?;
138 writeln!(f)?;
139 }
140 for strukt in &self.structs {
142 strukt.fmt(f)?;
143 writeln!(f)?;
144 }
145 for dclass in &self.dclasses {
147 dclass.fmt(f)?;
148 writeln!(f)?;
149 }
150 Ok(())
151 }
152}
153
154impl DCFileConfigAccessor for DCFile<'_> {
155 fn get_dc_config(&self) -> &DCFileConfig {
156 &self.config
157 }
158}
159
160impl LegacyDCHash for DCFile<'_> {
161 fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
162 if self.config.dc_virtual_inheritance {
163 if self.config.dc_sort_inheritance_by_file {
165 hashgen.add_int(1);
166 } else {
167 hashgen.add_int(2);
168 }
169 }
170 hashgen.add_int(self.get_num_dclasses().try_into().unwrap());
171
172 for strukt in &self.structs {
173 strukt.generate_hash(hashgen);
174 }
175
176 for dclass in &self.dclasses {
177 dclass.generate_hash(hashgen);
178 }
179 }
180}
181
182impl<'dc> DCFile<'dc> {
183 pub fn get_legacy_hash(&self) -> globals::DCFileHash {
190 if self.baked_legacy_hash != 0 {
191 self.baked_legacy_hash
192 } else {
193 let mut hashgen: DCHashGenerator = DCHashGenerator::default();
194
195 self.generate_hash(&mut hashgen);
196 hashgen.get_hash()
197 }
198 }
199
200 pub fn get_pretty_hash(&self) -> String {
202 format!("0x{:0width$x}", self.get_legacy_hash(), width = 8) }
204
205 pub fn get_num_imports(&self) -> usize {
208 self.imports.len()
209 }
210
211 pub fn get_python_import(&self, index: usize) -> &DCPythonImport {
212 self.imports.get(index).expect("Index out of bounds.")
213 }
214
215 pub fn get_num_keywords(&self) -> usize {
218 todo!();
219 }
220
221 pub fn get_keyword(&self, _index: usize) -> &'dc DCKeyword {
222 todo!();
223 }
224
225 pub fn has_keyword(&self, _keyword: String) -> bool {
226 todo!();
227 }
228
229 pub fn get_num_dclasses(&self) -> usize {
232 self.dclasses.len()
233 }
234
235 pub fn get_dclass(&self, _index: usize) -> &'dc DClass {
236 todo!();
237 }
238
239 pub fn get_dclass_by_id(&self, id: globals::DClassId) -> &'dc DClass {
240 self.dclasses.get(usize::from(id)).unwrap()
241 }
242
243 pub fn get_dclass_by_name(&self, _name: &str) -> &'dc DClass {
244 todo!();
245 }
246
247 pub fn get_num_structs(&self) -> usize {
250 todo!();
251 }
252
253 pub fn get_struct(&self, _index: usize) -> &'dc DCStruct {
254 todo!();
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn write_dc_python_import() {
264 let import: DCPythonImport = DCPythonImport {
265 module: "views".to_string(),
266 symbols: vec![],
267 };
268
269 assert_eq!(import.to_string(), "import views");
270 }
271
272 #[test]
273 fn write_dcfile_py_imports() {
274 let imports: Vec<DCPythonImport> = vec![
275 DCPythonImport {
276 module: "views".to_string(),
277 symbols: vec![],
278 },
279 DCPythonImport {
280 module: "views".to_string(),
281 symbols: vec!["DistributedDonut".to_string()],
282 },
283 DCPythonImport {
284 module: "views".to_string(),
285 symbols: vec!["Class".to_string(), "ClassAI".to_string(), "ClassOV".to_string()],
286 },
287 ];
288
289 let dcf: DCFile<'_> = DCFile {
290 config: DCFileConfig::default(),
291 baked_legacy_hash: 0_u32,
292 structs: vec![],
293 dclasses: vec![],
294 imports,
295 keywords: vec![],
296 type_defs: vec![],
297 field_id_2_field: vec![],
298 all_object_valid: false,
299 inherited_fields_stale: false,
300 };
301
302 assert_eq!(
303 dcf.to_string(),
304 "\
305 /*\n\
306 DC_MULTIPLE_INHERITANCE = true\n\
307 DC_SORT_INHERITANCE_BY_FILE = true\n\
308 DC_VIRTUAL_INHERITANCE = true\n\
309 */\n\n\
310 import views\n\
311 from views import DistributedDonut\n\
312 from views import Class, ClassAI, ClassOV\n\
313 \n\
314 ",
315 );
316 }
317}
318
319pub(crate) mod interim {
322 use super::{ast, globals, DCField, DCFileConfig};
323 use crate::dckeyword::interim::DCKeyword;
324 use crate::dclass::interim::DClass;
325 use crate::dcstruct::interim::DCStruct;
326 use crate::parser::error::{Diagnostic, SemanticError};
327 use crate::parser::pipeline::PipelineData;
328 use anyhow::{anyhow, Result};
329 use std::collections::HashSet;
330
331 #[derive(Debug)]
332 pub struct PythonImport {
333 pub module: String,
334 pub symbols: Vec<String>,
335 }
336
337 #[derive(Debug)]
339 pub(crate) struct DCFile {
340 pub config: DCFileConfig,
341 pub structs: Vec<DCStruct>,
342 pub dclasses: Vec<DClass>,
343 pub imports: Vec<PythonImport>,
344 pub keywords: Vec<DCKeyword>,
345 pub all_object_valid: bool,
348 pub inherited_fields_stale: bool,
349 }
350
351 impl From<DCFileConfig> for DCFile {
352 fn from(value: DCFileConfig) -> Self {
353 Self {
354 config: value,
355 structs: vec![],
356 dclasses: vec![],
357 imports: vec![],
358 keywords: vec![],
359 all_object_valid: true,
361 inherited_fields_stale: false,
362 }
363 }
364 }
365
366 impl DCFile {
367 pub fn add_field(&mut self, _field: DCField) {
369 todo!();
370 }
371
372 fn check_view_suffixes(pipeline: &mut PipelineData, view_suffixes: &ast::ViewSuffixes) {
377 let mut recorded_suffixes: HashSet<String> = HashSet::default();
378
379 for view_suffix in view_suffixes {
380 if !recorded_suffixes.insert(view_suffix.view.clone()) {
381 let diag: Diagnostic = Diagnostic::error(
382 view_suffix.span,
383 pipeline,
384 SemanticError::RedundantViewSuffix(view_suffix.view.clone()),
385 );
386
387 pipeline
388 .emit_diagnostic(diag.into())
389 .expect("Failed to emit diagnostic.");
390 }
391 }
392 }
393
394 pub fn add_python_import(&mut self, pipeline: &mut PipelineData, import: ast::PythonImport) {
398 let mut imports: Vec<PythonImport> = vec![];
399 let mut class_symbols: Vec<String> = vec![import.class.symbol.clone()];
400
401 Self::check_view_suffixes(pipeline, &import.module.symbol_views);
403 Self::check_view_suffixes(pipeline, &import.class.symbol_views);
404
405 if !import.class.symbol_views.is_empty() {
407 for class_suffix in &import.class.symbol_views {
408 class_symbols.push(import.class.symbol.clone() + &class_suffix.view);
409 }
410 }
411
412 if !import.module.symbol_views.is_empty() {
414 let mut c_symbol: String = class_symbols.first().unwrap().clone();
415
416 imports.push(PythonImport {
417 module: import.module.symbol.clone(),
418 symbols: vec![c_symbol],
419 });
420
421 for (i, module_suffix) in import.module.symbol_views.into_iter().enumerate() {
422 let full_import: String = import.module.symbol.clone() + &module_suffix.view;
423
424 if (class_symbols.len() - 1) <= i {
425 c_symbol = class_symbols.last().unwrap().clone();
426 } else {
427 c_symbol = class_symbols.get(i + 1).unwrap().clone();
428 }
429
430 imports.push(PythonImport {
431 module: full_import,
432 symbols: vec![c_symbol],
433 });
434 }
435 } else {
436 imports.push(PythonImport {
438 module: import.module.symbol,
439 symbols: class_symbols,
440 });
441 }
442
443 for imp in imports {
444 self.imports.push(imp);
445 }
446 }
447
448 pub fn add_keyword(&mut self, pipeline: &mut PipelineData, keyword: ast::KeywordDefinition) {
449 let new_kw: DCKeyword = keyword.into();
451
452 for kw in &self.keywords {
453 if kw.name == new_kw.name {
454 let diag: Diagnostic = Diagnostic::error(
455 new_kw.span,
456 pipeline,
457 SemanticError::AlreadyDefined(new_kw.name.clone()),
458 );
459
460 pipeline
461 .emit_diagnostic(diag.into())
462 .expect("Failed to emit diagnostic.");
463 return;
464 }
465 }
466 self.keywords.push(new_kw);
467 }
468
469 pub fn add_typedef(&mut self, _name: String) -> Result<(), ()> {
470 todo!();
471 }
472
473 pub fn add_dclass(&mut self, dclass: DClass) {
474 self.dclasses.push(dclass);
475 }
476
477 pub fn add_struct(&mut self, _strct: DCStruct) {
478 todo!();
479 }
480
481 pub fn get_next_dclass_id(
487 &mut self,
488 pipeline: &mut PipelineData,
489 dclass: &DClass, ) -> Result<globals::DClassId> {
491 let dc_num: u16 = self.dclasses.len().try_into().unwrap();
492
493 if dc_num == globals::DClassId::MAX {
494 let diag: Diagnostic =
496 Diagnostic::error(dclass.span, pipeline, SemanticError::DClassOverflow);
497
498 pipeline
499 .emit_diagnostic(diag.into())
500 .expect("Failed to emit diagnostic.");
501
502 return Err(anyhow!("Ran out of 16-bit DClass IDs!"));
503 }
504 Ok(dc_num - 1_u16)
505 }
506 }
507}