1use std::collections::{BTreeMap, BTreeSet};
30
31use zerodds_ast::node::ModuleNode;
32use zerodds_ast::{
33 AnyId, AppliedAnnotation, AppliedArgument, Ast, AutoId, Bound, CaseLabel, CharWidth, ConstExpr,
34 ConstExprNode, EvalError, Extensibility, Facets, Literal, MemberIdAssignment, MemberIdOrigin,
35 MemberSpec, PrimitiveKind, SourceFacet, TryConstruct, TypeKind, TypeNode, VerbatimDirective,
36 VerbatimPlacement, XTypesFacet, XmlFacet,
37};
38use zerodds_ast_core::{Id, PendingRef, Ref, ScopedName};
39use zerodds_xml::{DdsXmlDocument, XmlElement, parse_xml_tree};
40
41use crate::ComposeOptions;
42use crate::expr::{self, Expression};
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Severity {
47 Error,
50 Warning,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct Diagnostic {
63 pub severity: Severity,
65 pub message: String,
67 pub at: String,
70 pub spec_reference: &'static str,
72}
73
74impl std::fmt::Display for Diagnostic {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "{}: {} [{}]", self.at, self.message, self.spec_reference)
77 }
78}
79
80pub(crate) fn lower_all(
82 ast: &mut Ast,
83 sources: &[(String, String)],
84 options: &ComposeOptions,
85) -> Vec<Diagnostic> {
86 let mut diagnostics = Vec::new();
87 let units = load_units(sources, &options.include_paths, &mut diagnostics);
88
89 let mut lowering = Lowering {
90 ast,
91 options: options.clone(),
92 unit: String::new(),
93 diagnostics,
94 declared: BTreeMap::new(),
95 builtin: BTreeMap::new(),
96 };
97
98 for (name, doc) in &units {
102 lowering.unit = name.clone();
103 lowering.walk_units(doc, Pass::Declare);
104 }
105 for (name, doc) in &units {
106 lowering.unit = name.clone();
107 lowering.walk_units(doc, Pass::Define);
108 }
109 lowering.unit = units
110 .first()
111 .map_or_else(|| String::from("<bundle>"), |(name, _)| name.clone());
112 lowering.resolve();
113 lowering.complete();
114 lowering.diagnostics
115}
116
117fn load_units(
129 sources: &[(String, String)],
130 include_paths: &[std::path::PathBuf],
131 diagnostics: &mut Vec<Diagnostic>,
132) -> Vec<(String, DdsXmlDocument)> {
133 let mut units = Vec::with_capacity(sources.len());
134 let mut seen: BTreeSet<String> = sources.iter().map(|(name, _)| document_key(name)).collect();
137 for (name, text) in sources {
138 load_one(
139 name,
140 text,
141 &mut units,
142 &mut Loading {
143 seen: &mut seen,
144 include_paths,
145 },
146 diagnostics,
147 0,
148 );
149 }
150 units
151}
152
153const MAX_INCLUDE_DEPTH: usize = 32;
155
156struct Loading<'a> {
158 seen: &'a mut BTreeSet<String>,
160 include_paths: &'a [std::path::PathBuf],
163}
164
165fn locate_include(
169 including: &str,
170 file: &str,
171 include_paths: &[std::path::PathBuf],
172) -> std::path::PathBuf {
173 let beside = std::path::Path::new(including)
174 .parent()
175 .map_or_else(|| std::path::PathBuf::from(file), |dir| dir.join(file));
176 let mut candidates = vec![beside];
177 candidates.extend(include_paths.iter().map(|dir| dir.join(file)));
178 candidates
179 .iter()
180 .find(|candidate| candidate.is_file())
181 .cloned()
182 .unwrap_or_else(|| candidates.swap_remove(0))
183}
184
185fn load_one(
188 name: &str,
189 text: &str,
190 units: &mut Vec<(String, DdsXmlDocument)>,
191 loading: &mut Loading<'_>,
192 diagnostics: &mut Vec<Diagnostic>,
193 depth: usize,
194) {
195 let document = match parse_xml_tree(text) {
196 Ok(document) => document,
197 Err(error) => {
198 diagnostics.push(Diagnostic {
199 severity: Severity::Error,
200 message: format!("the document is not well-formed DDS-XML: {error:?}"),
201 at: name.to_owned(),
202 spec_reference: "OMG DDS-XML 1.0 §7.1",
203 });
204 return;
205 }
206 };
207 let mut files = Vec::new();
208 let mut deeper = false;
209 if let Ok(libraries) = document.library_elements("types") {
210 for library in libraries {
211 collect_includes(library, &mut files, 0, &mut deeper);
212 check_identifier_names(library, name, diagnostics);
213 }
214 }
215 if deeper {
216 diagnostics.push(Diagnostic {
217 severity: Severity::Error,
218 message: format!("modules nest deeper than {MAX_INCLUDE_DEPTH} levels; the includes below are not read"),
219 at: name.to_owned(),
220 spec_reference: "OMG DDS-XTypes 1.3 §7.3.2.1.1",
221 });
222 }
223 for file in files {
224 let path = locate_include(name, &file, loading.include_paths);
225 let included = path.to_string_lossy().into_owned();
226 if !std::path::Path::new(&file)
227 .extension()
228 .is_some_and(|extension| extension.eq_ignore_ascii_case("xml"))
229 {
230 diagnostics.push(Diagnostic {
231 severity: Severity::Error,
232 message: format!(
233 "`{file}` is not an XML document; including a file of another Type \
234 Representation is not supported, and §7.3.2.1.1 does not require it"
235 ),
236 at: format!("{name}: include[{file}]"),
237 spec_reference: "OMG DDS-XTypes 1.3 §7.3.2.1.1",
238 });
239 continue;
240 }
241 if !loading.seen.insert(document_key(&included)) {
242 continue;
243 }
244 if depth >= MAX_INCLUDE_DEPTH {
245 diagnostics.push(Diagnostic {
246 severity: Severity::Error,
247 message: format!("includes nest deeper than {MAX_INCLUDE_DEPTH} levels"),
248 at: format!("{name}: include[{file}]"),
249 spec_reference: "OMG DDS-XTypes 1.3 §7.3.2.1.1",
250 });
251 continue;
252 }
253 match std::fs::read_to_string(&path) {
254 Ok(included_text) => {
255 load_one(
256 &included,
257 &included_text,
258 units,
259 loading,
260 diagnostics,
261 depth + 1,
262 );
263 }
264 Err(error) => diagnostics.push(Diagnostic {
265 severity: Severity::Error,
266 message: format!("the included file cannot be read: {error}"),
267 at: format!("{name}: include[{file}]"),
268 spec_reference: "OMG DDS-XTypes 1.3 §7.3.2.1.1",
269 }),
270 }
271 }
272 units.push((name.to_owned(), document));
273}
274
275const IDENTIFIER_ATTRIBUTES: [&str; 4] = [
280 "name",
281 "baseType",
282 "nonBasicTypeName",
283 "mapKeyNonBasicTypeName",
284];
285
286fn check_identifier_names(library: &XmlElement, document: &str, diagnostics: &mut Vec<Diagnostic>) {
293 let mut stack: Vec<&XmlElement> = library.children.iter().collect();
294 while let Some(element) = stack.pop() {
295 for attribute in IDENTIFIER_ATTRIBUTES {
296 if let Some(value) = element.attribute(attribute) {
297 if !zerodds_xml::is_identifier_name(value) {
298 diagnostics.push(Diagnostic {
299 severity: Severity::Error,
300 message: format!(
301 "{}@{attribute}=\"{value}\" is no identifierName (a letter or `::` \
302 first, then letters, digits, `_` and `::`)",
303 element.path
304 ),
305 at: document.to_owned(),
306 spec_reference: "OMG DDS-XTypes 1.3 Annex A, identifierName",
307 });
308 }
309 }
310 }
311 stack.extend(element.children.iter());
312 }
313}
314
315fn collect_includes(element: &XmlElement, out: &mut Vec<String>, depth: usize, deeper: &mut bool) {
320 if depth > MAX_INCLUDE_DEPTH {
321 *deeper = true;
322 return;
323 }
324 for child in &element.children {
325 match child.name.as_str() {
326 "include" => {
327 if let Some(file) = child.attribute("file") {
328 out.push(file.to_owned());
329 }
330 }
331 "module" => collect_includes(child, out, depth + 1, deeper),
332 _ => {}
333 }
334 }
335}
336
337fn document_key(name: &str) -> String {
339 std::fs::canonicalize(name).map_or_else(
340 |_| name.to_owned(),
341 |path| path.to_string_lossy().into_owned(),
342 )
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
346enum Pass {
347 Declare,
348 Define,
349}
350
351struct Lowering<'a> {
352 ast: &'a mut Ast,
353 options: ComposeOptions,
354 unit: String,
355 diagnostics: Vec<Diagnostic>,
356 declared: BTreeMap<Vec<String>, Id<TypeNode>>,
358 builtin: BTreeMap<PrimitiveKind, Id<TypeNode>>,
362}
363
364#[derive(Clone)]
367struct Scope {
368 path: Vec<String>,
370 module: Id<ModuleNode>,
372 trail: String,
374}
375
376impl<'a> Lowering<'a> {
377 fn walk_units(&mut self, doc: &DdsXmlDocument, pass: Pass) {
380 let root = self.ast.root_module();
381 let scope = Scope {
382 path: Vec::new(),
383 module: root,
384 trail: format!("{}: {}", self.unit, doc.root.name),
385 };
386 match doc.library_elements("types") {
387 Ok(libraries) => {
388 for library in libraries {
389 let scope = Scope {
390 trail: format!("{}/types", scope.trail),
391 ..scope.clone()
392 };
393 if pass == Pass::Declare {
394 self.constants_in_order(&library.children, &scope, None);
395 }
396 for child in &library.children {
397 self.declaration(child, &scope, pass);
398 }
399 }
400 }
401 Err(error) => self.error(
402 &scope.trail,
403 format!("no <types> to read: {error:?}"),
404 "OMG DDS-XML 1.0 §7.3.3.3",
405 ),
406 }
407 }
408
409 fn constants_in_order(&mut self, children: &[XmlElement], scope: &Scope, within: Option<&str>) {
415 let mut remaining: Vec<&XmlElement> =
416 children.iter().filter(|c| c.name == "const").collect();
417 while !remaining.is_empty() {
418 let declared: Vec<String> = remaining
419 .iter()
420 .filter_map(|c| c.attribute("name").map(str::to_owned))
421 .collect();
422 let ready = remaining.iter().position(|c| {
423 let own = c.attribute("name");
424 let mut named = Vec::new();
425 if let Some(Ok(parsed)) = c.attribute("value").map(expr::parse) {
426 names_in(&parsed, &mut named);
427 }
428 !named
429 .iter()
430 .any(|name| Some(name.as_str()) != own && declared.contains(name))
431 });
432 let at = ready.unwrap_or(0);
433 let el = remaining.remove(at);
434 self.constant(el, scope, within);
435 }
436 }
437
438 fn declaration(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
440 match el.name.as_str() {
441 "module" => self.module(el, scope, pass),
442 "struct" => self.structure(el, scope, pass),
443 "union" => self.union(el, scope, pass),
444 "enum" => match pass {
445 Pass::Declare => self.enumeration(el, scope),
446 Pass::Define => self.annotate_enumerated(el, scope, "enumerator"),
449 },
450 "bitmask" => match pass {
451 Pass::Declare => self.bitmask(el, scope),
452 Pass::Define => self.annotate_enumerated(el, scope, "flag"),
453 },
454 "typedef" => self.alias(el, scope, pass),
455 "forward_dcl" | "forwardDcl" => {
456 if pass == Pass::Declare {
457 self.forward_declaration(el, scope);
458 }
459 }
460 "include" => {}
463 "const" => {}
466 "bitset" => self.bitset(el, scope, pass),
467 "annotation" => {
468 if pass == Pass::Declare {
469 self.annotation_declaration(el, scope);
470 }
471 }
472 other => {
473 if pass == Pass::Declare {
474 self.error(
475 &format!("{}/{other}", scope.trail),
476 format!("<{other}> is not a type declaration"),
477 "OMG DDS-XTypes 1.3 Annex A",
478 );
479 }
480 }
481 }
482 }
483
484 fn module(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
485 let Some(name) = self.named(el, scope) else {
486 return;
487 };
488 let Ok(module) = self.ast.ensure_module(scope.module, &name) else {
489 self.error(
490 &scope.trail,
491 format!("the model refused a module named `{name}`"),
492 "zerodds-ast-1.0 §3.1",
493 );
494 return;
495 };
496 let mut path = scope.path.clone();
497 path.push(name.clone());
498 let inner = Scope {
499 trail: format!("{}/module[{name}]", scope.trail),
500 path,
501 module,
502 };
503 if pass == Pass::Declare {
504 if let Ok(node_id) = self.ast.module(module).map(|node| node.node_id) {
505 self.set_xml_facet(AnyId::Module(module, node_id), el);
506 if let Some(autoid) = self.autoid_of(el, &inner.trail) {
511 if let Some(facets) = self.ast.facets_mut(AnyId::Module(module, node_id)) {
512 facets.xtypes_mut().autoid = Some(autoid);
513 }
514 }
515 }
516 }
517 if pass == Pass::Declare {
518 self.constants_in_order(&el.children, &inner, None);
519 }
520 for child in &el.children {
521 self.declaration(child, &inner, pass);
522 }
523 }
524
525 fn structure(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
528 let Some(name) = self.named(el, scope) else {
529 return;
530 };
531 let trail = format!("{}/struct[{name}]", scope.trail);
532 if pass == Pass::Declare {
533 let placeholder = TypeKind::Structure {
534 base: None,
535 members: Vec::new(),
536 };
537 self.declare(&name, scope, placeholder, el, &trail);
538 return;
539 }
540 let Some(id) = self.declared_here(&name, scope) else {
541 return;
542 };
543 let base = el
544 .attribute("baseType")
545 .or_else(|| el.attribute("base_type"))
546 .map(|written| self.named_ref(written, scope, &trail));
547 if self
548 .ast
549 .complete_type(
550 id,
551 TypeKind::Structure {
552 base,
553 members: Vec::new(),
554 },
555 )
556 .is_err()
557 {
558 self.error(
559 &trail,
560 format!("`{name}` is declared twice"),
561 "OMG IDL 4.2 §7.2.3",
562 );
563 return;
564 }
565 let mut inner_path = scope.path.clone();
568 inner_path.push(name.clone());
569 let inner = Scope {
570 path: inner_path,
571 ..scope.clone()
572 };
573 let annotations = self.applied_annotations(el, &inner, &trail);
574 let verbatim = self.verbatim_directives(el, &trail);
575 self.attach_to_type(id, annotations, verbatim);
576 self.constants_in_order(&el.children, &inner, Some(&name));
578 for child in &el.children {
579 match child.name.as_str() {
580 "member" => {
581 let spec = self.member_spec(child, &inner, &trail);
582 let Some(spec) = spec else { continue };
583 if self.ast.add_member(id, spec).is_err() {
584 self.error(
585 &trail,
586 String::from("the model refused a member"),
587 "zerodds-ast-1.0 §3.3",
588 );
589 }
590 }
591 "const" | "annotate" | "verbatim" => {}
594 other => self.warning(
595 &trail,
596 format!("<{other}> is no child of a <struct> and is not read"),
597 "OMG DDS-XTypes 1.3 Annex A",
598 ),
599 }
600 }
601 }
602
603 fn union(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
604 let Some(name) = self.named(el, scope) else {
605 return;
606 };
607 let trail = format!("{}/union[{name}]", scope.trail);
608 if pass == Pass::Declare {
609 let placeholder = TypeKind::Union {
610 discriminator: Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(
611 Vec::new(),
612 ))),
613 cases: Vec::new(),
614 default_case: None,
615 };
616 self.declare(&name, scope, placeholder, el, &trail);
617 return;
618 }
619 let Some(id) = self.declared_here(&name, scope) else {
620 return;
621 };
622 let Some(discriminator_el) = el.child("discriminator") else {
623 self.error(
624 &trail,
625 String::from("a union needs a <discriminator>"),
626 "OMG DDS-XTypes 1.3 Annex A",
627 );
628 return;
629 };
630 let discriminator_is_key = truthy(discriminator_el.attribute("key"));
641 if discriminator_is_key
642 && self.options.key_on_union_discriminator == crate::KeyOnUnionDiscriminator::Refused
643 {
644 self.error(
645 &trail,
646 String::from(
647 "`key=\"true\"` on a union discriminator is not carried under \
648 `KeyOnUnionDiscriminator::Refused`: the clause permits it and no measured \
649 implementation accepts it, so this setting reports it rather than \
650 computing a KeyHash no peer computes",
651 ),
652 "DDS-XTypes 1.3 §7.2.2.4.4.4.8, Table 21",
653 );
654 let _ = self.ast.complete_type(
661 id,
662 TypeKind::Union {
663 discriminator: broken("key discriminator"),
664 cases: Vec::new(),
665 default_case: None,
666 },
667 );
668 return;
669 }
670 let discriminator = self.type_reference(
671 discriminator_el,
672 scope,
673 &trail,
674 "type",
675 "nonBasicTypeName",
676 "stringMaxLength",
677 );
678 if self
679 .ast
680 .complete_type(
681 id,
682 TypeKind::Union {
683 discriminator: discriminator.clone(),
684 cases: Vec::new(),
685 default_case: None,
686 },
687 )
688 .is_err()
689 {
690 self.error(
691 &trail,
692 format!("`{name}` is declared twice"),
693 "OMG IDL 4.2 §7.2.3",
694 );
695 return;
696 }
697 if let Some(behaviour) = self.try_construct(discriminator_el, &trail) {
698 self.set_type_xtypes(id, |x| x.discriminator_try_construct = Some(behaviour));
699 }
700 let on_discriminator = self.applied_annotations(discriminator_el, scope, &trail);
704 if !on_discriminator.is_empty() {
705 self.set_type_xtypes(id, |x| x.discriminator_annotations.extend(on_discriminator));
706 }
707 let annotations = self.applied_annotations(el, scope, &trail);
708 let verbatim = self.verbatim_directives(el, &trail);
709 self.attach_to_type(id, annotations, verbatim);
710 if discriminator_is_key {
711 self.set_type_xtypes(id, |x| x.discriminator_is_key = true);
712 }
713
714 let discriminator_type = discriminator.resolved();
715 for case in el.children_named("case") {
716 let mut labels = Vec::new();
717 for label in case.children_named("caseDiscriminator") {
718 let Some(written) = label.attribute("value") else {
719 self.error(
720 &trail,
721 String::from("a <caseDiscriminator> needs a `value`"),
722 "OMG DDS-XTypes 1.3 Annex A",
723 );
724 continue;
725 };
726 match self.case_label(written, discriminator_type, &trail) {
727 Some(parsed) => labels.push(parsed),
728 None => {
734 labels.clear();
735 break;
736 }
737 }
738 }
739 if labels.is_empty() {
740 self.error(
741 &trail,
742 String::from("a <case> needs at least one <caseDiscriminator>"),
743 "OMG DDS-XTypes 1.3 Annex A",
744 );
745 continue;
746 }
747 let Some(member_el) = case.child("member") else {
748 self.error(
749 &trail,
750 String::from("a <case> needs a <member>"),
751 "OMG DDS-XTypes 1.3 Annex A",
752 );
753 continue;
754 };
755 let Some(spec) = self.member_spec(member_el, scope, &trail) else {
756 continue;
757 };
758 if self.ast.add_case(id, labels, spec).is_err() {
759 self.error(
760 &trail,
761 String::from("the model refused a case"),
762 "zerodds-ast-1.0 §3.3",
763 );
764 }
765 }
766 }
767
768 fn enumeration(&mut self, el: &XmlElement, scope: &Scope) {
769 let Some(name) = self.named(el, scope) else {
770 return;
771 };
772 let trail = format!("{}/enum[{name}]", scope.trail);
773 let kind = TypeKind::Enumeration {
774 enumerators: Vec::new(),
775 };
776 let Some(id) = self.declare_now(&name, scope, kind, el, &trail) else {
777 return;
778 };
779 for literal in el.children_named("enumerator") {
780 let Some(literal_name) = literal.attribute("name") else {
781 self.error(
782 &trail,
783 String::from("an <enumerator> needs a `name`"),
784 "OMG DDS-XTypes 1.3 Annex A",
785 );
786 continue;
787 };
788 let literal_name = literal_name.to_owned();
789 let value = match literal.attribute("value") {
794 Some(written) => {
800 match self.const_exp(written, scope, &trail, "OMG DDS-XTypes 1.3 §7.3.2.7.1") {
801 Some(expr) => Some(expr),
802 None => continue,
803 }
804 }
805 None => None,
806 };
807 let Ok(enumerator) = self.ast.add_enumerator(id, &literal_name, value) else {
808 self.error(
809 &trail,
810 format!("the model refused enumerator `{literal_name}`"),
811 "zerodds-ast-1.0 §3.3",
812 );
813 continue;
814 };
815 if truthy(literal.attribute("defaultLiteral"))
816 || truthy(literal.attribute("default_literal"))
817 {
818 if let Ok(node_id) = self.ast.enumerator(enumerator).map(|node| node.node_id) {
819 if let Some(facets) =
820 self.ast.facets_mut(AnyId::Enumerator(enumerator, node_id))
821 {
822 facets.xtypes_mut().is_default_literal = true;
823 }
824 }
825 }
826 }
827 }
828
829 fn bitmask(&mut self, el: &XmlElement, scope: &Scope) {
830 let Some(name) = self.named(el, scope) else {
831 return;
832 };
833 let trail = format!("{}/bitmask[{name}]", scope.trail);
834 let kind = TypeKind::Bitmask { bits: Vec::new() };
835 let Some(id) = self.declare_now(&name, scope, kind, el, &trail) else {
836 return;
837 };
838 let bits: Vec<&XmlElement> = el
844 .children
845 .iter()
846 .filter(|c| c.name == "flag" || c.name == "bit_value" || c.name == "bit")
847 .collect();
848 for bit in bits {
849 let Some(bit_name) = bit.attribute("name") else {
850 self.error(
851 &trail,
852 format!("a <{}> needs a `name`", bit.name),
853 "OMG DDS-XTypes 1.3 Annex A",
854 );
855 continue;
856 };
857 let bit_name = bit_name.to_owned();
858 let written = bit.attribute("position").or_else(|| bit.attribute("value"));
859 let position = match written {
860 Some(written) => match integer(written).and_then(|v| u16::try_from(v).ok()) {
861 Some(position) => Some(position),
862 None => {
863 self.error(
864 &trail,
865 format!("`{written}` is not a bit position"),
866 "OMG DDS-XTypes 1.3 §7.3.1.2.1.9",
867 );
868 continue;
869 }
870 },
871 None => None,
872 };
873 if self.ast.add_bit_value(id, &bit_name, position).is_err() {
874 self.error(
875 &trail,
876 format!("the model refused bit `{bit_name}`"),
877 "zerodds-ast-1.0 §3.3",
878 );
879 }
880 }
881 }
882
883 fn alias(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
884 let Some(name) = self.named(el, scope) else {
885 return;
886 };
887 let trail = format!("{}/typedef[{name}]", scope.trail);
888 if pass == Pass::Declare {
889 let placeholder = TypeKind::Alias {
890 aliased: Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(Vec::new()))),
891 };
892 self.declare(&name, scope, placeholder, el, &trail);
893 return;
894 }
895 let Some(id) = self.declared_here(&name, scope) else {
896 return;
897 };
898 let aliased = self.type_reference(
899 el,
900 scope,
901 &trail,
902 "type",
903 "nonBasicTypeName",
904 "stringMaxLength",
905 );
906 let aliased = self.collections_around(el, aliased, scope, &trail);
907 if self
908 .ast
909 .complete_type(id, TypeKind::Alias { aliased })
910 .is_err()
911 {
912 self.error(
913 &trail,
914 format!("`{name}` is declared twice"),
915 "OMG IDL 4.2 §7.2.3",
916 );
917 }
918 }
919
920 fn forward_declaration(&mut self, el: &XmlElement, scope: &Scope) {
926 let Some(name) = self.named(el, scope) else {
927 return;
928 };
929 let trail = format!("{}/forward_dcl[{name}]", scope.trail);
930 let kind = match el.attribute("kind").unwrap_or("STRUCT") {
931 "UNION" | "union" => TypeKind::Union {
932 discriminator: Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(
933 Vec::new(),
934 ))),
935 cases: Vec::new(),
936 default_case: None,
937 },
938 _ => TypeKind::Structure {
939 base: None,
940 members: Vec::new(),
941 },
942 };
943 self.declare(&name, scope, kind, el, &trail);
944 }
945
946 fn member_spec(&mut self, el: &XmlElement, scope: &Scope, trail: &str) -> Option<MemberSpec> {
950 let name = match el.attribute("name") {
951 Some(name) => name.to_owned(),
952 None => {
953 self.error(
954 trail,
955 String::from("a <member> needs a `name`"),
956 "OMG DDS-XTypes 1.3 Annex A",
957 );
958 return None;
959 }
960 };
961 let trail = format!("{trail}/member[{name}]");
962 let element = self.type_reference(
963 el,
964 scope,
965 &trail,
966 "type",
967 "nonBasicTypeName",
968 "stringMaxLength",
969 );
970 let type_ = self.collections_around(el, element, scope, &trail);
971
972 let collection = el.attribute("sequenceMaxLength").is_some()
979 || el.attribute("mapKeyType").is_some()
980 || el.attribute("arrayDimensions").is_some();
981 let external = truthy(el.attribute("external"));
982 let mut xtypes = XTypesFacet {
983 is_key: truthy(el.attribute("key")),
984 is_optional: truthy(el.attribute("optional")),
985 is_external: external && !collection,
986 element_is_external: external && collection,
987 is_must_understand: truthy(el.attribute("mustUnderstand"))
988 || truthy(el.attribute("must_understand")),
989 is_non_serialized: truthy(el.attribute("nonSerialized")),
990 ..XTypesFacet::default()
991 };
992 if let Some(behaviour) = self.try_construct(el, &trail) {
997 if el
998 .attribute("arrayDimensions")
999 .is_some_and(|written| !written.trim().is_empty())
1000 {
1001 xtypes.element_try_construct = Some(behaviour);
1002 } else {
1003 xtypes.try_construct = Some(behaviour);
1004 }
1005 }
1006 if let Some(written) = el.attribute("id") {
1011 match integer(written).and_then(|v| u32::try_from(v).ok()) {
1012 Some(id) => {
1013 xtypes.member_id = Some(MemberIdAssignment {
1014 effective: id,
1015 origin: MemberIdOrigin::Declared,
1016 });
1017 }
1018 None => self.error(
1019 &trail,
1020 format!("`{written}` is not a member id"),
1021 "DDS-XTypes 1.3 §7.3.1.2.1.1",
1022 ),
1023 }
1024 } else if let Some(written) = el.attribute("hashid") {
1025 let source_name = if written.is_empty() || written.eq_ignore_ascii_case("true") {
1029 name.clone()
1030 } else {
1031 written.to_owned()
1032 };
1033 xtypes.member_id = Some(MemberIdAssignment {
1034 effective: 0,
1035 origin: MemberIdOrigin::Hashed { source_name },
1036 });
1037 }
1038
1039 let applied = self.applied_annotations(el, scope, &trail);
1042 let mut facets = self.facets(el, Some(xtypes));
1043 facets.annotations.extend(applied);
1044 Some(MemberSpec {
1045 name,
1046 type_,
1047 facets,
1048 })
1049 }
1050
1051 fn type_reference(
1055 &mut self,
1056 el: &XmlElement,
1057 scope: &Scope,
1058 trail: &str,
1059 type_attribute: &str,
1060 nonbasic_attribute: &str,
1061 bound_attribute: &str,
1062 ) -> Ref<TypeNode> {
1063 let Some(written) = el.attribute(type_attribute) else {
1064 self.error(
1065 trail,
1066 format!("no `{type_attribute}` attribute to read a type from"),
1067 "OMG DDS-XTypes 1.3 Annex A",
1068 );
1069 return broken(type_attribute);
1070 };
1071 if !matches!(written, "nonBasic" | "nonbasic") && el.attribute(nonbasic_attribute).is_some()
1074 {
1075 self.error(
1076 trail,
1077 format!(
1078 "`{nonbasic_attribute}` on an element whose `{type_attribute}` is \
1079 `{written}`, which is not `nonBasic`"
1080 ),
1081 "OMG DDS-XTypes 1.3 §7.3.2.2",
1082 );
1083 return broken(nonbasic_attribute);
1084 }
1085 match written {
1086 "nonBasic" | "nonbasic" => match el.attribute(nonbasic_attribute) {
1087 Some(name) => self.named_ref(name, scope, trail),
1088 None => {
1089 self.error(
1090 trail,
1091 format!(
1092 "`{type_attribute}=\"nonBasic\"` without a `{nonbasic_attribute}` \
1093 names nothing"
1094 ),
1095 "OMG DDS-XTypes 1.3 Annex A",
1096 );
1097 broken(nonbasic_attribute)
1098 }
1099 },
1100 "string" | "wstring" => {
1101 let width = if written == "string" {
1102 CharWidth::Char
1103 } else {
1104 CharWidth::WChar
1105 };
1106 let bound = self.bound(el.attribute(bound_attribute), scope, trail);
1107 self.anonymous(TypeKind::String { width, bound })
1108 }
1109 other => match primitive_of(other) {
1110 Some(kind) => self.primitive_ref(kind),
1111 None => {
1112 self.named_ref(other, scope, trail)
1117 }
1118 },
1119 }
1120 }
1121
1122 fn collections_around(
1135 &mut self,
1136 el: &XmlElement,
1137 element: Ref<TypeNode>,
1138 scope: &Scope,
1139 trail: &str,
1140 ) -> Ref<TypeNode> {
1141 let mut current = element;
1142 if let Some(written) = el.attribute("sequenceMaxLength") {
1143 let bound = self.bound(Some(written), scope, trail);
1144 current = self.anonymous(TypeKind::Sequence {
1145 element: current,
1146 bound,
1147 });
1148 }
1149 if let Some(written) = el.attribute("arrayDimensions") {
1150 match self.dimensions(written, scope, trail) {
1151 Some(dims) if !dims.is_empty() => {
1152 current = self.anonymous(TypeKind::Array {
1153 element: current,
1154 dims,
1155 });
1156 }
1157 Some(_) => {}
1158 None => return broken("arrayDimensions"),
1159 }
1160 }
1161 if let Some(key_written) = el.attribute("mapKeyType") {
1162 let key = self.type_reference(
1163 el,
1164 scope,
1165 trail,
1166 "mapKeyType",
1167 "mapKeyNonBasicTypeName",
1168 "mapKeyStringMaxLength",
1169 );
1170 let key_non_basic = matches!(key_written, "nonBasic" | "nonbasic");
1176 if !key_non_basic && el.attribute("mapKeyNonBasicTypeName").is_some() {
1177 self.error(
1178 trail,
1179 format!(
1180 "`mapKeyNonBasicTypeName` is not allowed beside the basic \
1181 `mapKeyType=\"{key_written}\"`"
1182 ),
1183 "OMG DDS-XTypes 1.3 §7.3.2.4.3",
1184 );
1185 }
1186 if !key_non_basic
1187 && !matches!(key_written, "string" | "wstring")
1188 && el.attribute("mapKeyStringMaxLength").is_some()
1189 {
1190 self.error(
1191 trail,
1192 format!(
1193 "`mapKeyStringMaxLength` is not allowed beside the key type \
1194 `{key_written}`, which is no string"
1195 ),
1196 "OMG DDS-XTypes 1.3 §7.3.2.4.3",
1197 );
1198 }
1199 if el.attribute("mapMaxLength").is_none() {
1201 self.error(
1202 trail,
1203 String::from("a map member needs `mapMaxLength`; \"-1\" is unbounded"),
1204 "OMG DDS-XTypes 1.3 §7.3.2.4.3",
1205 );
1206 }
1207 let bound = self.bound(el.attribute("mapMaxLength"), scope, trail);
1208 current = self.anonymous(TypeKind::Map {
1209 key,
1210 value: current,
1211 bound,
1212 });
1213 } else if el.attribute("mapMaxLength").is_some() {
1214 self.error(
1215 trail,
1216 String::from("`mapMaxLength` without `mapKeyType` names no key type"),
1217 "OMG DDS-XTypes 1.3 §7.3.2.4.3",
1218 );
1219 }
1220 current
1221 }
1222
1223 fn dimensions(
1228 &mut self,
1229 written: &str,
1230 scope: &Scope,
1231 trail: &str,
1232 ) -> Option<Vec<Id<ConstExprNode>>> {
1233 let parsed = match expr::parse_list(written) {
1234 Ok(parsed) => parsed,
1235 Err(why) => {
1236 self.error(
1237 trail,
1238 format!("`arrayDimensions=\"{written}\"` is not a list of bounds: {why}"),
1239 "OMG DDS-XTypes 1.3 §7.3.2.4.1",
1240 );
1241 return None;
1242 }
1243 };
1244 let mut out = Vec::with_capacity(parsed.len());
1245 for one in &parsed {
1246 let id = self.expression(one, scope, trail)?;
1247 match self.ast.eval_integral(id) {
1248 Ok(value) if value <= 0 => {
1249 self.error(
1250 trail,
1251 format!("an array dimension of {value} is no size"),
1252 "OMG DDS-XTypes 1.3 §7.3.2.4.1",
1253 );
1254 return None;
1255 }
1256 Ok(_) => {}
1257 Err(EvalError::Unresolved { .. }) => {}
1260 Err(why) => {
1261 self.error(
1262 trail,
1263 format!("an array dimension cannot be evaluated: {why:?}"),
1264 "OMG DDS-XTypes 1.3 §7.3.2.4.1",
1265 );
1266 return None;
1267 }
1268 }
1269 out.push(id);
1270 }
1271 Some(out)
1272 }
1273
1274 fn named_ref(&mut self, written: &str, scope: &Scope, trail: &str) -> Ref<TypeNode> {
1277 let absolute = written.starts_with("::");
1278 let segments: Vec<String> = written
1279 .split("::")
1280 .filter(|s| !s.is_empty())
1281 .map(str::to_owned)
1282 .collect();
1283 if segments.is_empty() {
1284 self.error(
1285 trail,
1286 String::from("an empty type name names nothing"),
1287 "OMG IDL 4.2 §7.5",
1288 );
1289 return broken("");
1290 }
1291 if absolute {
1292 if let Some(id) = self.declared.get(&segments) {
1293 return Ref::Resolved(*id);
1294 }
1295 return Ref::Pending(PendingRef::ScopedName(ScopedName::absolute(segments)));
1296 }
1297 for depth in (0..=scope.path.len()).rev() {
1298 let mut candidate: Vec<String> = scope.path.iter().take(depth).cloned().collect();
1299 candidate.extend(segments.iter().cloned());
1300 if let Some(id) = self.declared.get(&candidate) {
1301 return Ref::Resolved(*id);
1302 }
1303 }
1304 Ref::Pending(PendingRef::ScopedName(ScopedName::relative(segments)))
1308 }
1309
1310 fn primitive_ref(&mut self, kind: PrimitiveKind) -> Ref<TypeNode> {
1311 if let Some(id) = self.builtin.get(&kind) {
1312 return Ref::Resolved(*id);
1313 }
1314 let root = self.ast.root_module();
1315 match self
1316 .ast
1317 .add_type(root, kind.as_str(), TypeKind::Primitive(kind))
1318 {
1319 Ok(id) => {
1320 self.builtin.insert(kind, id);
1321 Ref::Resolved(id)
1322 }
1323 Err(_) => broken(kind.as_str()),
1324 }
1325 }
1326
1327 fn anonymous(&mut self, kind: TypeKind) -> Ref<TypeNode> {
1330 let root = self.ast.root_module();
1331 let what = kind.as_str();
1332 self.ast
1333 .add_type(root, "", kind)
1334 .map_or_else(|_| broken(what), Ref::Resolved)
1335 }
1336
1337 fn bound(&mut self, written: Option<&str>, scope: &Scope, trail: &str) -> Bound {
1349 let Some(written) = written else {
1350 return Bound::Unbounded;
1351 };
1352 let Some(id) = self.const_exp(written, scope, trail, "OMG DDS-XTypes 1.3 §7.3.2.4") else {
1353 return Bound::Unbounded;
1354 };
1355 match self.ast.eval_integral(id) {
1356 Ok(value) if value < -1 => {
1357 self.error(
1358 trail,
1359 format!("`{written}` is a negative bound other than -1"),
1360 "OMG DDS-XTypes 1.3 §7.3.2.4",
1361 );
1362 Bound::Unbounded
1363 }
1364 Ok(-1) => Bound::Unbounded,
1365 Ok(0) if self.options.zero_bound_is_unbounded => Bound::Unbounded,
1366 Ok(0) => {
1367 self.error(
1368 trail,
1369 format!(
1370 "`{written}` is a bound of zero; \"-1\" is unbounded, and a bound is \
1371 a positive integer"
1372 ),
1373 "OMG DDS-XTypes 1.3 §7.3.2.4",
1374 );
1375 Bound::Unbounded
1376 }
1377 Ok(_) | Err(EvalError::Unresolved { .. }) => Bound::Const(id),
1378 Err(why) => {
1379 self.error(
1380 trail,
1381 format!("`{written}` cannot be evaluated as a bound: {why:?}"),
1382 "OMG DDS-XTypes 1.3 §7.3.2.4",
1383 );
1384 Bound::Unbounded
1385 }
1386 }
1387 }
1388
1389 fn try_construct(&mut self, el: &XmlElement, trail: &str) -> Option<TryConstruct> {
1390 let written = el
1391 .attribute("tryConstruct")
1392 .or_else(|| el.attribute("try_construct"))?;
1393 match written.to_ascii_lowercase().as_str() {
1394 "trim" => Some(TryConstruct::Trim),
1395 "discard" => Some(TryConstruct::Discard),
1396 "use_default" | "usedefault" | "default" => Some(TryConstruct::UseDefault),
1397 other => {
1398 self.error(
1399 trail,
1400 format!("`{other}` is not a try-construct behaviour"),
1401 "DDS-XTypes 1.3 §7.3.1.2.1.12",
1402 );
1403 None
1404 }
1405 }
1406 }
1407
1408 fn case_label(
1410 &mut self,
1411 written: &str,
1412 discriminator: Option<Id<TypeNode>>,
1413 trail: &str,
1414 ) -> Option<CaseLabel> {
1415 if written.eq_ignore_ascii_case("default") {
1416 return Some(CaseLabel::Default);
1417 }
1418 if let Some(discriminator) = discriminator {
1422 let enumerators = self.ast.enumerators_of(discriminator).to_vec();
1423 for enumerator in enumerators {
1424 if self
1425 .ast
1426 .enumerator(enumerator)
1427 .is_ok_and(|node| node.name == written)
1428 {
1429 return match self.ast.add_expr(ConstExpr::Literal(Literal::Enumerator(
1430 Ref::Resolved(enumerator),
1431 ))) {
1432 Ok(expr) => Some(CaseLabel::Value(expr)),
1433 Err(_) => {
1434 self.error(
1435 trail,
1436 format!("the model refused the case label `{written}`"),
1437 "zerodds-ast-1.0 §3.5",
1438 );
1439 None
1440 }
1441 };
1442 }
1443 }
1444 }
1445 if let Some(value) = integer(written) {
1446 return match self.ast.add_int_expr(value) {
1447 Ok(expr) => Some(CaseLabel::Value(expr)),
1448 Err(_) => {
1449 self.error(
1450 trail,
1451 format!("the model refused the case label `{written}`"),
1452 "zerodds-ast-1.0 §3.5",
1453 );
1454 None
1455 }
1456 };
1457 }
1458 let literal = match written {
1459 "TRUE" | "true" => Some(Literal::Boolean(true)),
1460 "FALSE" | "false" => Some(Literal::Boolean(false)),
1461 _ => None,
1462 };
1463 if let Some(literal) = literal {
1464 return match self.ast.add_expr(ConstExpr::Literal(literal)) {
1465 Ok(expr) => Some(CaseLabel::Value(expr)),
1466 Err(_) => {
1467 self.error(
1468 trail,
1469 format!("the model refused the case label `{written}`"),
1470 "zerodds-ast-1.0 §3.5",
1471 );
1472 None
1473 }
1474 };
1475 }
1476 self.error(
1477 trail,
1478 format!("`{written}` is not a case label the discriminator can take"),
1479 "OMG IDL 4.2 §7.4.1.4.4.3",
1480 );
1481 None
1482 }
1483
1484 fn named(&mut self, el: &XmlElement, scope: &Scope) -> Option<String> {
1487 match el.attribute("name") {
1488 Some(name) if !name.is_empty() => Some(name.to_owned()),
1489 _ => {
1490 let what = el.name.clone();
1491 self.error(
1492 &scope.trail,
1493 format!("<{what}> needs a `name`"),
1494 "OMG DDS-XML 1.0 §7.1.4",
1495 );
1496 None
1497 }
1498 }
1499 }
1500
1501 fn declare(&mut self, name: &str, scope: &Scope, kind: TypeKind, el: &XmlElement, trail: &str) {
1503 let mut path = scope.path.clone();
1504 path.push(name.to_owned());
1505 if self.declared.contains_key(&path) {
1506 return;
1510 }
1511 match self.ast.add_forward_type(scope.module, name, kind) {
1512 Ok(id) => {
1513 self.declared.insert(path, id);
1514 self.record(id, el, trail);
1515 }
1516 Err(_) => self.error(
1517 trail,
1518 format!("the model refused a type named `{name}`"),
1519 "zerodds-ast-1.0 §3.2",
1520 ),
1521 }
1522 }
1523
1524 fn declare_now(
1527 &mut self,
1528 name: &str,
1529 scope: &Scope,
1530 kind: TypeKind,
1531 el: &XmlElement,
1532 trail: &str,
1533 ) -> Option<Id<TypeNode>> {
1534 let mut path = scope.path.clone();
1535 path.push(name.to_owned());
1536 match self.ast.add_type(scope.module, name, kind) {
1537 Ok(id) => {
1538 self.declared.insert(path, id);
1539 self.record(id, el, trail);
1540 Some(id)
1541 }
1542 Err(_) => {
1543 self.error(
1544 trail,
1545 format!("the model refused a type named `{name}`"),
1546 "zerodds-ast-1.0 §3.2",
1547 );
1548 None
1549 }
1550 }
1551 }
1552
1553 fn declared_here(&mut self, name: &str, scope: &Scope) -> Option<Id<TypeNode>> {
1554 let mut path = scope.path.clone();
1555 path.push(name.to_owned());
1556 self.declared.get(&path).copied()
1557 }
1558
1559 fn record(&mut self, id: Id<TypeNode>, el: &XmlElement, trail: &str) {
1563 let Ok(node_id) = self.ast.type_(id).map(|node| node.node_id) else {
1564 return;
1565 };
1566 let any = AnyId::Type(id, node_id);
1567 let mut xtypes = XTypesFacet {
1574 is_nested: truthy(el.attribute("nested")),
1575 nested_explicit: el.attribute("nested").is_some(),
1576 ignore_literal_names: truthy(el.attribute("ignoreLiteralNames")),
1577 ..XTypesFacet::default()
1578 };
1579 if let Some(written) = el.attribute("extensibility") {
1580 match extensibility_of(written) {
1581 Some(extensibility) => xtypes.extensibility = Some(extensibility),
1582 None => self.error(
1583 trail,
1584 format!("`{written}` is not final, appendable or mutable"),
1585 "DDS-XTypes 1.3 §7.3.1.2.1.8",
1586 ),
1587 }
1588 }
1589 if let Some(written) = el
1590 .attribute("bitBound")
1591 .or_else(|| el.attribute("bit_bound"))
1592 {
1593 match integer(written).and_then(|v| u16::try_from(v).ok()) {
1594 Some(bound) => xtypes.bit_bound = Some(bound),
1595 None => self.error(
1596 trail,
1597 format!("`{written}` is not a bit bound"),
1598 "DDS-XTypes 1.3 §7.3.1.2.1.9",
1599 ),
1600 }
1601 }
1602 xtypes.autoid = self.autoid_of(el, trail);
1603 let facets = self.facets(el, Some(xtypes));
1604 if let Some(slot) = self.ast.facets_mut(any) {
1605 *slot = facets;
1606 }
1607 }
1608
1609 fn autoid_of(&mut self, el: &XmlElement, trail: &str) -> Option<AutoId> {
1611 let written = el.attribute("autoid")?;
1612 match written.to_ascii_lowercase().as_str() {
1613 "hash" => Some(AutoId::Hash),
1614 "sequential" | "sequencial" => Some(AutoId::Sequential),
1618 other => {
1619 self.error(
1620 trail,
1621 format!("`{other}` is not HASH or SEQUENTIAL"),
1622 "DDS-XTypes 1.3 §7.3.1.2.1.1",
1623 );
1624 None
1625 }
1626 }
1627 }
1628
1629 fn facets(&self, el: &XmlElement, xtypes: Option<XTypesFacet>) -> Facets {
1630 Facets {
1631 source: SourceFacet::file(self.unit.clone(), None),
1632 xtypes,
1633 xml: Some(XmlFacet {
1634 element: el.name.clone(),
1635 attributes: el
1636 .attributes
1637 .iter()
1638 .map(|(k, v)| (k.clone(), v.clone()))
1639 .collect(),
1640 ..XmlFacet::default()
1641 }),
1642 ..Facets::default()
1643 }
1644 }
1645
1646 fn set_xml_facet(&mut self, any: AnyId, el: &XmlElement) {
1647 let facets = self.facets(el, None);
1648 if let Some(slot) = self.ast.facets_mut(any) {
1649 *slot = facets;
1650 }
1651 }
1652
1653 fn set_type_xtypes(&mut self, id: Id<TypeNode>, apply: impl FnOnce(&mut XTypesFacet)) {
1654 let Ok(node_id) = self.ast.type_(id).map(|node| node.node_id) else {
1655 return;
1656 };
1657 if let Some(facets) = self.ast.facets_mut(AnyId::Type(id, node_id)) {
1658 apply(facets.xtypes_mut());
1659 }
1660 }
1661
1662 fn resolve(&mut self) {
1667 let Ok(report) = self.ast.resolve_all_by_name() else {
1668 self.error(
1669 &self.unit.clone(),
1670 String::from("the model could not run a resolve pass"),
1671 "OMG IDL 4.2 §7.5",
1672 );
1673 return;
1674 };
1675 let broken: Vec<String> = report
1676 .broken
1677 .iter()
1678 .map(|(node, cause)| format!("{cause:?} (node {node:?})"))
1679 .collect();
1680 for one in broken {
1681 self.error(
1682 &self.unit.clone(),
1683 format!("a name does not resolve: {one}"),
1684 "OMG IDL 4.2 §7.5",
1685 );
1686 }
1687 let pending = self.ast.pending_count();
1688 if pending > 0 {
1689 self.error(
1690 &self.unit.clone(),
1691 format!("{pending} type reference(s) never resolved"),
1692 "OMG IDL 4.2 §7.5",
1693 );
1694 }
1695 }
1696
1697 fn complete(&mut self) {
1704 let types: Vec<Id<TypeNode>> = self.ast.types().map(|(id, _)| id).collect();
1705 for id in self.base_first(&types) {
1706 self.complete_one(id);
1707 }
1708 }
1709
1710 fn complete_one(&mut self, id: Id<TypeNode>) {
1711 let Ok(node) = self.ast.type_(id) else {
1712 return;
1713 };
1714 let node_id = node.node_id;
1715 let any = AnyId::Type(id, node_id);
1716 let is_aggregate = matches!(
1717 node.kind,
1718 TypeKind::Structure { .. } | TypeKind::Union { .. } | TypeKind::Valuetype { .. }
1719 );
1720 if !is_aggregate {
1721 return;
1722 }
1723 let derives = matches!(node.kind, TypeKind::Structure { base: Some(_), .. });
1730 if let (Some(extensibility), false) = (self.options.default_extensibility, derives) {
1731 let declared = self
1732 .ast
1733 .facets(any)
1734 .and_then(|facets| facets.xtypes.as_ref())
1735 .and_then(|x| x.extensibility);
1736 if declared.is_none() {
1737 if let Some(facets) = self.ast.facets_mut(any) {
1738 facets.xtypes_mut().extensibility = Some(extensibility);
1739 }
1740 }
1741 }
1742 self.member_ids(id);
1743 }
1744
1745 fn member_ids(&mut self, id: Id<TypeNode>) {
1750 let autoid = self.ast.effective_autoid(id);
1751 let members: Vec<_> = self.ast.members_of(id).to_vec();
1752 let mut previous: Option<u32> = self.last_base_member_id(id);
1753 for member in members {
1754 let Ok(node) = self.ast.member(member) else {
1755 continue;
1756 };
1757 let member_any = AnyId::Member(member, node.node_id);
1758 let name = node.name.clone();
1759 let declared = self
1760 .ast
1761 .facets(member_any)
1762 .and_then(|facets| facets.xtypes.as_ref())
1763 .and_then(|x| x.member_id.clone());
1764 let assignment = match declared {
1765 Some(assignment) if assignment.origin == MemberIdOrigin::Declared => assignment,
1766 Some(MemberIdAssignment {
1767 origin: MemberIdOrigin::Hashed { source_name },
1768 ..
1769 }) => MemberIdAssignment {
1770 effective: zerodds_idl_lower::hashed_member_id(&source_name),
1771 origin: MemberIdOrigin::Hashed { source_name },
1772 },
1773 _ => match autoid {
1774 Some(AutoId::Hash) => MemberIdAssignment {
1775 effective: zerodds_idl_lower::hashed_member_id(&name),
1776 origin: MemberIdOrigin::AutoHash,
1777 },
1778 Some(AutoId::Sequential) => MemberIdAssignment {
1779 effective: next_id(previous),
1780 origin: MemberIdOrigin::AutoSequential,
1781 },
1782 None => MemberIdAssignment {
1783 effective: next_id(previous),
1784 origin: MemberIdOrigin::PreviousPlusOne,
1785 },
1786 },
1787 };
1788 let hashed = matches!(
1789 assignment.origin,
1790 MemberIdOrigin::Hashed { .. } | MemberIdOrigin::AutoHash
1791 );
1792 if !hashed
1793 || self.options.hashed_id_advances_the_counter
1794 == zerodds_idl_lower::HashedIdCounter::Advances
1795 {
1796 previous = Some(assignment.effective);
1797 }
1798 if let Some(facets) = self.ast.facets_mut(member_any) {
1799 facets.xtypes_mut().member_id = Some(assignment);
1800 }
1801 }
1802 }
1803
1804 fn last_base_member_id(&self, id: Id<TypeNode>) -> Option<u32> {
1805 let base = self.ast.base_of(id)?;
1806 let last = *self.ast.all_members_of(base).last()?;
1807 let node_id = self.ast.member(last).ok()?.node_id;
1808 self.ast
1809 .facets(AnyId::Member(last, node_id))
1810 .and_then(|facets| facets.xtypes.as_ref())
1811 .and_then(|x| x.member_id.as_ref())
1812 .map(|assignment| assignment.effective)
1813 }
1814
1815 fn base_first(&self, types: &[Id<TypeNode>]) -> Vec<Id<TypeNode>> {
1818 let mut out: Vec<Id<TypeNode>> = Vec::with_capacity(types.len());
1819 for id in types.iter().copied() {
1820 self.push_base_first(id, &mut out, 0);
1821 }
1822 out
1823 }
1824
1825 fn push_base_first(&self, id: Id<TypeNode>, out: &mut Vec<Id<TypeNode>>, depth: usize) {
1826 if depth > MAX_INHERITANCE_DEPTH || out.contains(&id) {
1827 return;
1828 }
1829 if let Some(base) = self.ast.base_of(id) {
1830 self.push_base_first(base, out, depth + 1);
1831 }
1832 out.push(id);
1833 }
1834
1835 fn warning(&mut self, at: &str, message: String, spec_reference: &'static str) {
1836 self.diagnostics.push(Diagnostic {
1837 severity: Severity::Warning,
1838 message,
1839 at: at.to_owned(),
1840 spec_reference,
1841 });
1842 }
1843
1844 fn const_exp(
1848 &mut self,
1849 written: &str,
1850 scope: &Scope,
1851 trail: &str,
1852 spec_reference: &'static str,
1853 ) -> Option<Id<ConstExprNode>> {
1854 match expr::parse(written) {
1855 Ok(parsed) => self.expression(&parsed, scope, trail),
1856 Err(why) => {
1857 self.error(
1858 trail,
1859 format!("`{written}` is not a <const_exp>: {why}"),
1860 spec_reference,
1861 );
1862 None
1863 }
1864 }
1865 }
1866
1867 fn expression(
1872 &mut self,
1873 parsed: &Expression,
1874 scope: &Scope,
1875 trail: &str,
1876 ) -> Option<Id<ConstExprNode>> {
1877 let node = match parsed {
1878 Expression::Integer(value) => ConstExpr::Literal(Literal::Integer(*value)),
1879 Expression::Float { mantissa, exponent } => ConstExpr::Literal(Literal::Float {
1880 mantissa: mantissa.clone(),
1881 exponent: exponent.clone(),
1882 }),
1883 Expression::String(text) => ConstExpr::Literal(Literal::String(text.clone())),
1884 Expression::Char(c) => ConstExpr::Literal(Literal::Char(*c)),
1885 Expression::Boolean(value) => ConstExpr::Literal(Literal::Boolean(*value)),
1886 Expression::Name { absolute, segments } => {
1887 match self.lookup(*absolute, segments, scope, |any| {
1888 matches!(any, AnyId::Const(..) | AnyId::Enumerator(..))
1889 }) {
1890 Some(AnyId::Const(id, _)) => ConstExpr::NameRef(Ref::Resolved(id)),
1891 Some(AnyId::Enumerator(id, _)) => {
1892 ConstExpr::Literal(Literal::Enumerator(Ref::Resolved(id)))
1893 }
1894 _ => ConstExpr::NameRef(Ref::Pending(PendingRef::ScopedName(scoped(
1895 *absolute, segments,
1896 )))),
1897 }
1898 }
1899 Expression::Unary(op, inner) => ConstExpr::Unary {
1900 op: *op,
1901 operand: self.expression(inner, scope, trail)?,
1902 },
1903 Expression::Binary(op, left, right) => {
1904 let lhs = self.expression(left, scope, trail)?;
1905 let rhs = self.expression(right, scope, trail)?;
1906 ConstExpr::Binary { op: *op, lhs, rhs }
1907 }
1908 };
1909 match self.ast.add_expr(node) {
1910 Ok(id) => Some(id),
1911 Err(_) => {
1912 self.error(
1913 trail,
1914 String::from("the model refused an expression"),
1915 "zerodds-ast-1.0 §3.5",
1916 );
1917 None
1918 }
1919 }
1920 }
1921
1922 fn lookup(
1926 &self,
1927 absolute: bool,
1928 segments: &[String],
1929 scope: &Scope,
1930 wanted: impl Fn(&AnyId) -> bool,
1931 ) -> Option<AnyId> {
1932 let depths: Vec<usize> = if absolute {
1933 vec![0]
1934 } else {
1935 (0..=scope.path.len()).rev().collect()
1936 };
1937 for depth in depths {
1938 let mut candidate: Vec<String> = scope.path.iter().take(depth).cloned().collect();
1939 candidate.extend(segments.iter().cloned());
1940 if let Ok(any) = self
1941 .ast
1942 .find_by_scoped_name(&ScopedName::absolute(candidate))
1943 {
1944 if wanted(&any) {
1945 return Some(any);
1946 }
1947 }
1948 }
1949 None
1950 }
1951
1952 fn constant(&mut self, el: &XmlElement, scope: &Scope, within: Option<&str>) {
1962 let Some(name) = self.named(el, scope) else {
1963 return;
1964 };
1965 let trail = format!("{}/const[{name}]", scope.trail);
1966 let type_ = self.type_reference(
1967 el,
1968 scope,
1969 &trail,
1970 "type",
1971 "nonBasicTypeName",
1972 "stringMaxLength",
1973 );
1974 let Some(written) = el.attribute("value") else {
1975 self.error(
1976 &trail,
1977 String::from("a <const> needs a `value`"),
1978 "OMG DDS-XTypes 1.3 Annex A",
1979 );
1980 return;
1981 };
1982 let Some(expression) =
1983 self.const_exp(written, scope, &trail, "OMG DDS-XTypes 1.3 §7.3.2.1.3")
1984 else {
1985 return;
1986 };
1987 let Ok(id) = self.ast.add_const(scope.module, &name, type_, expression) else {
1988 self.error(
1989 &trail,
1990 format!("the model refused a constant named `{name}`"),
1991 "zerodds-ast-1.0 §3.5",
1992 );
1993 return;
1994 };
1995 let Ok(node_id) = self.ast.constant(id).map(|c| c.node_id) else {
1996 return;
1997 };
1998 let any = AnyId::Const(id, node_id);
1999 if let Some(owner) = within {
2000 if self.ast.set_declaration_scope(any, owner).is_err() {
2001 self.error(
2002 &trail,
2003 format!("the model refused the scope `{owner}` for `{name}`"),
2004 "zerodds-ast-1.0 §3.5",
2005 );
2006 }
2007 }
2008 self.set_xml_facet(any, el);
2009 }
2010
2011 fn bitset(&mut self, el: &XmlElement, scope: &Scope, pass: Pass) {
2015 let Some(name) = self.named(el, scope) else {
2016 return;
2017 };
2018 let trail = format!("{}/bitset[{name}]", scope.trail);
2019 if pass == Pass::Declare {
2020 let placeholder = TypeKind::Bitset {
2021 base: None,
2022 fields: Vec::new(),
2023 };
2024 self.declare(&name, scope, placeholder, el, &trail);
2025 return;
2026 }
2027 let Some(id) = self.declared_here(&name, scope) else {
2028 return;
2029 };
2030 let base = el
2031 .attribute("baseType")
2032 .or_else(|| el.attribute("base_type"))
2033 .map(|written| self.named_ref(written, scope, &trail));
2034 if self
2035 .ast
2036 .complete_type(
2037 id,
2038 TypeKind::Bitset {
2039 base,
2040 fields: Vec::new(),
2041 },
2042 )
2043 .is_err()
2044 {
2045 self.error(
2046 &trail,
2047 format!("`{name}` is declared twice"),
2048 "OMG IDL 4.2 §7.2.3",
2049 );
2050 return;
2051 }
2052 let annotations = self.applied_annotations(el, scope, &trail);
2053 let verbatim = self.verbatim_directives(el, &trail);
2054 self.attach_to_type(id, annotations, verbatim);
2055 for field in &el.children {
2056 if field.name != "bitfield" {
2057 if !matches!(field.name.as_str(), "annotate" | "verbatim") {
2058 self.warning(
2059 &trail,
2060 format!("<{}> is no child of a <bitset> and is not read", field.name),
2061 "OMG IDL 4.2 §7.4.13.4.3",
2062 );
2063 }
2064 continue;
2065 }
2066 let field_name = field.attribute("name").map(str::to_owned);
2067 let label = field_name
2068 .clone()
2069 .unwrap_or_else(|| String::from("<padding>"));
2070 let width = match (field.attribute("bits"), field.attribute("mask")) {
2071 (Some(written), _) => integer(written).and_then(|v| u16::try_from(v).ok()),
2072 (None, Some(mask)) => integer(mask)
2073 .and_then(|v| u64::try_from(v).ok())
2074 .and_then(|v| u16::try_from(v.count_ones()).ok()),
2075 (None, None) => None,
2076 };
2077 let Some(width) = width.filter(|w| (1..=64).contains(w)) else {
2078 self.error(
2079 &trail,
2080 format!("bitfield `{label}` needs a `bits` or a `mask` of 1 to 64 bits"),
2081 "OMG IDL 4.2 §7.4.13.4.3",
2082 );
2083 continue;
2084 };
2085 let holder = field.attribute("type").is_some().then(|| {
2086 self.type_reference(
2087 field,
2088 scope,
2089 &trail,
2090 "type",
2091 "nonBasicTypeName",
2092 "stringMaxLength",
2093 )
2094 });
2095 match self
2096 .ast
2097 .add_bitfield(id, field_name.as_deref(), width, holder)
2098 {
2099 Ok(bitfield) => {
2100 if let Ok(node_id) = self.ast.bitfield(bitfield).map(|b| b.node_id) {
2101 self.set_xml_facet(AnyId::Bitfield(bitfield, node_id), field);
2102 }
2103 }
2104 Err(_) => self.error(
2105 &trail,
2106 format!("the model refused bitfield `{label}`"),
2107 "zerodds-ast-1.0 §3.3",
2108 ),
2109 }
2110 }
2111 }
2112
2113 fn annotation_declaration(&mut self, el: &XmlElement, scope: &Scope) {
2116 let Some(name) = self.named(el, scope) else {
2117 return;
2118 };
2119 let trail = format!("{}/annotation[{name}]", scope.trail);
2120 let Ok(def) = self.ast.add_annotation_def(scope.module, &name) else {
2121 self.error(
2122 &trail,
2123 format!("the model refused an annotation named `{name}`"),
2124 "zerodds-ast-1.0 §3.6",
2125 );
2126 return;
2127 };
2128 if let Ok(node_id) = self.ast.annotation_def(def).map(|d| d.node_id) {
2129 self.set_xml_facet(AnyId::AnnotationDef(def, node_id), el);
2130 }
2131 if let Some(written) = el.attribute("baseType") {
2136 let absolute = written.starts_with("::");
2137 let segments: Vec<String> = written
2138 .split("::")
2139 .filter(|s| !s.is_empty())
2140 .map(str::to_owned)
2141 .collect();
2142 let base = self.lookup(absolute, &segments, scope, |any| {
2143 matches!(any, AnyId::AnnotationDef(..))
2144 });
2145 let Some(AnyId::AnnotationDef(base, _)) = base else {
2146 self.error(
2147 &trail,
2148 format!(
2149 "`baseType=\"{written}\"` names no annotation declared before this one"
2150 ),
2151 "OMG DDS-XTypes 1.3 Annex A",
2152 );
2153 return;
2154 };
2155 let inherited: Vec<_> = match self.ast.annotation_def(base) {
2156 Ok(node) => node.members.clone(),
2157 Err(_) => {
2158 self.error(
2159 &trail,
2160 format!("the model does not hold the base annotation `{written}`"),
2161 "zerodds-ast-1.0 §3.6",
2162 );
2163 return;
2164 }
2165 };
2166 for member in inherited {
2167 let Ok(node) = self.ast.annotation_member(member) else {
2168 self.error(
2169 &trail,
2170 format!("a member of the base annotation `{written}` is not in the model"),
2171 "zerodds-ast-1.0 §3.6",
2172 );
2173 continue;
2174 };
2175 let (member_name, member_type, member_default) =
2176 (node.name.clone(), node.type_.clone(), node.default);
2177 if self
2178 .ast
2179 .add_annotation_member(def, &member_name, member_type, member_default)
2180 .is_err()
2181 {
2182 self.error(
2183 &trail,
2184 format!(
2185 "the model refused the inherited annotation member `{member_name}`"
2186 ),
2187 "zerodds-ast-1.0 §3.6",
2188 );
2189 }
2190 }
2191 }
2192 for child in &el.children {
2193 if child.name != "member" {
2194 self.warning(
2195 &trail,
2196 format!(
2197 "<{}> is no child of an <annotation> and is not read",
2198 child.name
2199 ),
2200 "OMG DDS-XTypes 1.3 Annex A",
2201 );
2202 continue;
2203 }
2204 let Some(member) = child.attribute("name") else {
2205 self.error(
2206 &trail,
2207 String::from("a <member> needs a `name`"),
2208 "OMG DDS-XTypes 1.3 Annex A",
2209 );
2210 continue;
2211 };
2212 let member = member.to_owned();
2213 let type_ = self.type_reference(
2214 child,
2215 scope,
2216 &format!("{trail}/member[{member}]"),
2217 "type",
2218 "nonBasicTypeName",
2219 "stringMaxLength",
2220 );
2221 if self
2222 .ast
2223 .add_annotation_member(def, &member, type_, None)
2224 .is_err()
2225 {
2226 self.error(
2227 &trail,
2228 format!("the model refused annotation member `{member}`"),
2229 "zerodds-ast-1.0 §3.6",
2230 );
2231 }
2232 }
2233 }
2234
2235 fn applied_annotations(
2240 &mut self,
2241 el: &XmlElement,
2242 scope: &Scope,
2243 trail: &str,
2244 ) -> Vec<AppliedAnnotation> {
2245 let mut out = Vec::new();
2246 for note in el.children_named("annotate") {
2247 let Some(written) = note.attribute("name") else {
2248 self.error(
2249 trail,
2250 String::from("an <annotate> needs a `name`"),
2251 "OMG DDS-XTypes 1.3 Annex A",
2252 );
2253 continue;
2254 };
2255 let at = format!("{trail}/annotate[{written}]");
2256 let absolute = written.starts_with("::");
2257 let segments: Vec<String> = written
2258 .split("::")
2259 .filter(|s| !s.is_empty())
2260 .map(str::to_owned)
2261 .collect();
2262 let user_declared = self.lookup(absolute, &segments, scope, |any| {
2263 matches!(any, AnyId::AnnotationDef(..))
2264 });
2265 let definition = match user_declared {
2266 Some(AnyId::AnnotationDef(id, _)) => Ref::Resolved(id),
2267 _ => {
2268 if segments.len() == 1
2269 && zerodds_ast::node::annotation::BUILTIN_ANNOTATIONS.contains(&written)
2270 {
2271 self.error(
2272 &at,
2273 format!(
2274 "`{written}` is a built-in annotation; this representation \
2275 writes it as an attribute of the element"
2276 ),
2277 "OMG DDS-XTypes 1.3 §7.3.2.5",
2278 );
2279 continue;
2280 }
2281 self.error(
2285 &at,
2286 format!("no <annotation> named `{written}` is declared"),
2287 "OMG DDS-XTypes 1.3 §7.3.2.9",
2288 );
2289 continue;
2290 }
2291 };
2292 let mut arguments = Vec::new();
2293 for argument in note.children_named("member") {
2294 let (Some(member), Some(value)) =
2295 (argument.attribute("name"), argument.attribute("value"))
2296 else {
2297 self.error(
2298 &at,
2299 String::from("an annotation <member> needs a `name` and a `value`"),
2300 "OMG DDS-XTypes 1.3 Annex A",
2301 );
2302 continue;
2303 };
2304 let member = member.to_owned();
2305 if let Some(expression) =
2306 self.const_exp(value, scope, &at, "OMG DDS-XTypes 1.3 §7.3.2.9")
2307 {
2308 arguments.push(AppliedArgument {
2309 name: Some(member),
2310 value: expression,
2311 });
2312 }
2313 }
2314 out.push(AppliedAnnotation {
2315 name: written.to_owned(),
2316 definition,
2317 arguments,
2318 });
2319 }
2320 out
2321 }
2322
2323 fn verbatim_directives(&mut self, el: &XmlElement, trail: &str) -> Vec<VerbatimDirective> {
2329 let mut out = Vec::new();
2330 for verbatim in el.children_named("verbatim") {
2331 let placement = match verbatim.attribute("placement") {
2332 Some(written) => match zerodds_idl_lower::verbatim_placement(written) {
2333 Some(placement) => placement,
2334 None => {
2335 self.error(
2336 trail,
2337 format!("`{written}` is none of the six `@verbatim` placements"),
2338 "OMG IDL 4.2 §8.3.5.1",
2339 );
2340 continue;
2341 }
2342 },
2343 None => VerbatimPlacement::BeforeDeclaration,
2344 };
2345 let text = verbatim
2346 .child("text")
2347 .map_or_else(|| verbatim.text.clone(), |t| t.text.clone());
2348 out.push(VerbatimDirective {
2349 language: verbatim
2350 .attribute("language")
2351 .map_or_else(|| String::from("*"), str::to_owned),
2352 placement,
2353 text,
2354 });
2355 }
2356 out
2357 }
2358
2359 fn attach_to_type(
2361 &mut self,
2362 id: Id<TypeNode>,
2363 annotations: Vec<AppliedAnnotation>,
2364 verbatim: Vec<VerbatimDirective>,
2365 ) {
2366 let Ok(node_id) = self.ast.type_(id).map(|node| node.node_id) else {
2367 return;
2368 };
2369 if let Some(facets) = self.ast.facets_mut(AnyId::Type(id, node_id)) {
2370 facets.annotations.extend(annotations);
2371 if !verbatim.is_empty() {
2372 facets.xtypes_mut().verbatim.extend(verbatim);
2373 }
2374 }
2375 }
2376
2377 fn annotate_enumerated(&mut self, el: &XmlElement, scope: &Scope, literal: &str) {
2381 let Some(name) = el.attribute("name") else {
2382 return;
2383 };
2384 let name = name.to_owned();
2385 let trail = format!("{}/{}[{name}]", scope.trail, el.name);
2386 let Some(id) = self.declared_here(&name, scope) else {
2387 return;
2388 };
2389 let annotations = self.applied_annotations(el, scope, &trail);
2390 let verbatim = self.verbatim_directives(el, &trail);
2391 self.attach_to_type(id, annotations, verbatim);
2392 let Ok(node) = self.ast.type_(id) else {
2393 return;
2394 };
2395 let literals: Vec<(String, AnyId)> = match &node.kind {
2396 TypeKind::Enumeration { enumerators } => enumerators
2397 .iter()
2398 .filter_map(|e| {
2399 let node = self.ast.enumerator(*e).ok()?;
2400 Some((node.name.clone(), AnyId::Enumerator(*e, node.node_id)))
2401 })
2402 .collect(),
2403 TypeKind::Bitmask { bits } => bits
2404 .iter()
2405 .filter_map(|b| {
2406 let node = self.ast.bit_value(*b).ok()?;
2407 Some((node.name.clone(), AnyId::BitValue(*b, node.node_id)))
2408 })
2409 .collect(),
2410 _ => Vec::new(),
2411 };
2412 for child in el.children_named(literal) {
2413 let Some(literal_name) = child.attribute("name") else {
2414 continue;
2415 };
2416 let applied = self.applied_annotations(child, scope, &trail);
2417 if applied.is_empty() {
2418 continue;
2419 }
2420 if let Some((_, any)) = literals.iter().find(|(n, _)| n == literal_name) {
2421 if let Some(facets) = self.ast.facets_mut(*any) {
2422 facets.annotations.extend(applied);
2423 }
2424 }
2425 }
2426 }
2427
2428 fn error(&mut self, at: &str, message: String, spec_reference: &'static str) {
2429 self.diagnostics.push(Diagnostic {
2430 severity: Severity::Error,
2431 message,
2432 at: at.to_owned(),
2433 spec_reference,
2434 });
2435 }
2436}
2437
2438fn names_in(parsed: &Expression, out: &mut Vec<String>) {
2445 match parsed {
2446 Expression::Name { segments, .. } => {
2447 if let Some(last) = segments.last() {
2448 out.push(last.clone());
2449 }
2450 }
2451 Expression::Unary(_, inner) => names_in(inner, out),
2452 Expression::Binary(_, left, right) => {
2453 names_in(left, out);
2454 names_in(right, out);
2455 }
2456 _ => {}
2457 }
2458}
2459
2460fn scoped(absolute: bool, segments: &[String]) -> ScopedName {
2461 if absolute {
2462 ScopedName::absolute(segments.to_vec())
2463 } else {
2464 ScopedName::relative(segments.to_vec())
2465 }
2466}
2467
2468const MAX_INHERITANCE_DEPTH: usize = 64;
2471
2472fn next_id(previous: Option<u32>) -> u32 {
2473 previous.map_or(0, |last| last.saturating_add(1))
2474}
2475
2476fn broken(what: &str) -> Ref<TypeNode> {
2482 Ref::Broken(zerodds_ast_core::BreakCause::NotFound {
2483 what: PendingRef::ScopedName(ScopedName::absolute(vec![what.to_owned()])),
2484 })
2485}
2486
2487fn primitive_of(written: &str) -> Option<PrimitiveKind> {
2497 Some(match written {
2498 "boolean" | "bool" => PrimitiveKind::Boolean,
2499 "byte" | "octet" => PrimitiveKind::Octet,
2500 "uint8" => PrimitiveKind::UInt8,
2501 "int8" => PrimitiveKind::Int8,
2502 "char8" | "char" => PrimitiveKind::Char,
2503 "char16" | "wchar" => PrimitiveKind::WChar,
2504 "int16" | "short" => PrimitiveKind::Int16,
2505 "uint16" | "ushort" | "unsignedShort" => PrimitiveKind::UInt16,
2506 "int32" | "long" => PrimitiveKind::Int32,
2507 "uint32" | "ulong" | "unsignedLong" => PrimitiveKind::UInt32,
2508 "int64" | "longlong" | "longLong" => PrimitiveKind::Int64,
2509 "uint64" | "ulonglong" | "unsignedLongLong" => PrimitiveKind::UInt64,
2510 "float32" | "float" => PrimitiveKind::Float32,
2511 "float64" | "double" => PrimitiveKind::Float64,
2512 "float128" | "longdouble" | "longDouble" => PrimitiveKind::Float128,
2513 _ => return None,
2514 })
2515}
2516
2517const _: () = ();
2526
2527fn extensibility_of(written: &str) -> Option<Extensibility> {
2528 match written.to_ascii_lowercase().as_str() {
2529 "final" => Some(Extensibility::Final),
2530 "appendable" | "extensible" => Some(Extensibility::Appendable),
2531 "mutable" => Some(Extensibility::Mutable),
2532 _ => None,
2533 }
2534}
2535
2536fn truthy(written: Option<&str>) -> bool {
2537 matches!(
2538 written.map(str::trim),
2539 Some("true" | "TRUE" | "True" | "1" | "yes")
2540 )
2541}
2542
2543fn integer(written: &str) -> Option<i128> {
2546 let text = written.trim();
2547 let (negative, rest) = match text.strip_prefix('-') {
2548 Some(rest) => (true, rest),
2549 None => (false, text.strip_prefix('+').unwrap_or(text)),
2550 };
2551 let value = if let Some(hex) = rest.strip_prefix("0x").or_else(|| rest.strip_prefix("0X")) {
2552 i128::from_str_radix(hex, 16).ok()?
2553 } else if let Some(bin) = rest.strip_prefix("0b").or_else(|| rest.strip_prefix("0B")) {
2554 i128::from_str_radix(bin, 2).ok()?
2555 } else {
2556 rest.parse::<i128>().ok()?
2557 };
2558 Some(if negative { -value } else { value })
2559}
2560
2561#[cfg(test)]
2562#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
2563mod tests {
2564 use super::*;
2565
2566 #[test]
2567 fn an_integer_reads_decimal_hexadecimal_and_a_sign() {
2568 assert_eq!(integer("10"), Some(10));
2569 assert_eq!(integer("0x0e"), Some(14));
2570 assert_eq!(integer("0X1F"), Some(31));
2571 assert_eq!(integer("-1"), Some(-1));
2572 assert_eq!(integer("VAL0"), None);
2573 }
2574
2575 #[test]
2576 fn the_two_primitive_vocabularies_agree() {
2577 assert_eq!(primitive_of("int32"), primitive_of("long"));
2578 assert_eq!(primitive_of("byte"), primitive_of("octet"));
2579 assert_eq!(primitive_of("float128"), primitive_of("longdouble"));
2580 assert_eq!(primitive_of("int8"), Some(PrimitiveKind::Int8));
2581 assert_eq!(primitive_of("nonBasic"), None);
2582 }
2583
2584 #[test]
2591 fn octet_and_uint8_are_two_kinds() {
2592 assert_eq!(primitive_of("octet"), Some(PrimitiveKind::Octet));
2593 assert_eq!(primitive_of("byte"), Some(PrimitiveKind::Octet));
2594 assert_eq!(primitive_of("uint8"), Some(PrimitiveKind::UInt8));
2595 assert_ne!(primitive_of("octet"), primitive_of("uint8"));
2596 }
2597}