1use crate::default_common::{discriminator_default_value, resolve_alias, selected_case};
44use crate::emit::CodecTarget;
45use crate::emit::node::Node;
46use crate::encode::EncodeError;
47use crate::fixed_literal::zero_decimal;
48use zerodds_ast::{
49 Ast, ConstExpr, Literal as AstLiteral, MemberNode, PrimitiveKind, TypeKind, TypeNode,
50};
51use zerodds_ast_core::Id;
52use zerodds_lang_cst::common::{Doc, Literal};
53use zerodds_lang_cst::node::{Expr, Function, Stat};
54
55const MAX_ALIAS_DEPTH: usize = 64;
58
59#[must_use]
65pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
66 match ast.type_(id).map(|node| node.kind.clone()) {
67 Ok(TypeKind::Primitive(kind)) => {
68 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
69 }
70 _ => format!("zd_def_{}", Node::type_name(ast, id)),
71 }
72}
73
74#[must_use]
77pub fn defaulter_from_decoder(decoder: &str) -> String {
78 match decoder.strip_prefix("zd_dec_") {
79 Some(rest) => format!("zd_def_{rest}"),
80 None => format!("zd_def_{decoder}"),
81 }
82}
83
84#[must_use]
86pub fn default_into(ast: &Ast, id: Id<TypeNode>, place: Expr) -> Stat {
87 Stat::Assign {
88 doc: Doc::none(),
89 place,
90 value: Expr::call(&defaulter_name(ast, id), Vec::new()),
91 }
92}
93
94pub fn member_default(
102 ast: &Ast,
103 node: &MemberNode,
104 type_: Id<TypeNode>,
105 place: Expr,
106) -> Result<Vec<Stat>, EncodeError> {
107 Ok(vec![Stat::Assign {
108 doc: Doc::none(),
109 place,
110 value: member_value(ast, node, type_)?,
111 }])
112}
113
114pub fn node_defaulters_called_by(ast: &Ast, decls: &[Stat]) -> Result<Vec<Stat>, EncodeError> {
122 let index: Vec<(String, Id<TypeNode>)> = ast
123 .types()
124 .map(|(id, _)| (defaulter_name(ast, id), id))
125 .collect();
126 let mut called: Vec<String> = Vec::new();
127 for decl in decls {
128 calls_in_stat(decl, &mut called);
129 }
130 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
131 let mut have: Vec<String> = Vec::new();
132 for _ in 0..=index.len() {
133 let mut grew = false;
134 for name in &called {
135 if have.contains(name) {
136 continue;
137 }
138 have.push(name.clone());
139 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
140 wanted.push(id);
141 grew = true;
142 }
143 }
144 let produced = node_defaulters_over(ast, &wanted)?;
145 if !grew {
146 return Ok(produced);
147 }
148 for decl in &produced {
149 calls_in_stat(decl, &mut called);
150 }
151 }
152 node_defaulters_over(ast, &wanted)
153}
154
155pub fn node_defaulters_over(ast: &Ast, types: &[Id<TypeNode>]) -> Result<Vec<Stat>, EncodeError> {
160 let mut out = Vec::new();
161 let mut emitted: Vec<String> = Vec::new();
162 for id in types.iter().copied() {
163 let name = defaulter_name(ast, id);
164 if emitted.contains(&name) {
165 continue;
166 }
167 let Some(value) = value_of(ast, id, &name)? else {
168 continue;
169 };
170 emitted.push(name.clone());
171 out.push(Stat::Function(Function {
172 doc: Doc::line(
173 "Table 9 of DDS-XTypes 1.3: what this type is where the stream did not carry it.",
174 ),
175 name: zerodds_lang_cst::common::Ident::new(&name),
176 params: Vec::new(),
177 body: vec![Stat::Return(Some(value))],
178 }));
179 }
180 Ok(out)
181}
182
183#[allow(clippy::too_many_lines)]
186fn value_of(ast: &Ast, id: Id<TypeNode>, own_name: &str) -> Result<Option<Expr>, EncodeError> {
187 let id = resolve_alias(ast, id);
191 let refuse = |kind: &'static str| EncodeError::NoWriter {
192 member: own_name.to_string(),
193 kind,
194 };
195 let Ok(type_node) = ast.type_(id) else {
202 return Err(refuse("a type the model does not hold"));
203 };
204 let value = match &type_node.kind {
205 TypeKind::Primitive(kind) => primitive_default(*kind),
206
207 TypeKind::String { .. } => Expr::string(""),
209
210 TypeKind::Fixed { scale, .. } => Expr::string(&zero_decimal(*scale)),
212
213 TypeKind::Alias { .. } => {
214 return Err(refuse("typedef with an unresolved target"));
215 }
216
217 TypeKind::Enumeration { .. } => {
221 let Some(enumerator) = crate::emit::default_enumerator_name(ast, id) else {
222 return Err(refuse(
223 "enumeration with no enumerator to take as its default",
224 ));
225 };
226 Node::enumerator_constant(ast, id, &enumerator)
227 }
228
229 TypeKind::Bitmask { .. } => {
233 if ast.effective_bit_bound(id) > 32 {
234 Node::wide_unsigned(0)
235 } else {
236 Expr::int(0)
237 }
238 }
239
240 TypeKind::Bitset { .. } => {
243 let mut entries = Vec::new();
244 for field in &ast.all_bitfields_of(id) {
245 let Ok(field_node) = ast.bitfield(*field) else {
250 return Err(refuse("a bitfield the model does not hold"));
251 };
252 let Some(field_name) = field_node.name.as_ref() else {
254 continue;
255 };
256 entries.push((
257 zerodds_lang_cst::common::Ident::new(field_name),
258 Expr::int(0),
259 ));
260 }
261 Expr::Object(entries)
262 }
263
264 TypeKind::Sequence { .. } | TypeKind::Map { .. } => Expr::Array(Vec::new()),
269
270 TypeKind::Array { element, dims } => {
276 let Some(element) = element.resolved() else {
277 return Err(refuse("array with an unresolved element"));
278 };
279 let mut lengths = Vec::new();
280 for dim in dims {
281 let length = ast
282 .eval_integral(*dim)
283 .map_err(|_| refuse("array whose length does not evaluate"))?;
284 lengths.push(
285 usize::try_from(length)
286 .map_err(|_| refuse("array of a length this column cannot spell"))?,
287 );
288 }
289 let mut out = Expr::call(&defaulter_name(ast, element), Vec::new());
290 for length in lengths.into_iter().rev() {
291 out = Expr::Array(vec![out; length]);
292 }
293 out
294 }
295
296 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
299 let mut entries = Vec::new();
300 if let Some(base) = crate::adapter::c::struct_base(ast, id) {
302 entries.push((
303 zerodds_lang_cst::common::Ident::new("parent"),
304 Expr::call(&defaulter_name(ast, base), Vec::new()),
305 ));
306 }
307 for member_id in ast.members_of(id) {
308 let Ok(member_node) = ast.member(*member_id) else {
314 return Err(refuse("a member the model does not hold"));
315 };
316 if member_node.facets.is_optional() {
318 continue;
319 }
320 let Some(member_type) = member_node.type_.resolved() else {
321 return Err(EncodeError::NoWriter {
322 member: member_node.name.clone(),
323 kind: "member with an unresolved type",
324 });
325 };
326 entries.push((
327 zerodds_lang_cst::common::Ident::new(&member_node.name),
328 member_value(ast, member_node, member_type)?,
329 ));
330 }
331 Expr::Object(entries)
332 }
333
334 TypeKind::Union { discriminator, .. } => {
340 let Some(discriminator) = discriminator.resolved() else {
341 return Err(refuse("union with an unresolved discriminator"));
342 };
343 let selector = discriminator_default_value(ast, discriminator)
349 .ok_or_else(|| refuse("union whose discriminator type has no default value"))?;
350 let mut entries = vec![(
351 zerodds_lang_cst::common::Ident::new("_d"),
352 discriminant_literal(ast, discriminator, selector),
353 )];
354 if let Some((member_type, field)) = selected_by_default(ast, id, selector)? {
355 entries.push((
356 zerodds_lang_cst::common::Ident::new(&field),
357 Expr::call(&defaulter_name(ast, member_type), Vec::new()),
358 ));
359 }
360 Expr::Object(entries)
361 }
362
363 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
364 return Ok(None);
365 }
366 TypeKind::ValueBox { .. } => {
367 return Err(refuse(
368 "boxed value, whose presence flag has no settled wire form",
369 ));
370 }
371 TypeKind::Any => {
372 return Err(refuse(
373 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
374 as a TypeCode ahead of the value",
375 ));
376 }
377 TypeKind::ValueBase => {
378 return Err(refuse(
379 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
380 one, as a chunked value with its repository id",
381 ));
382 }
383 };
384 Ok(Some(value))
385}
386
387fn member_value(ast: &Ast, node: &MemberNode, type_: Id<TypeNode>) -> Result<Expr, EncodeError> {
390 match node.facets.xtypes.as_ref().and_then(|x| x.default_value) {
391 Some(expr) => explicit(ast, expr, type_, &node.name),
392 None => Ok(Expr::call(&defaulter_name(ast, type_), Vec::new())),
393 }
394}
395
396fn primitive_default(kind: PrimitiveKind) -> Expr {
398 match kind {
399 PrimitiveKind::Boolean => Expr::bool(false),
400 PrimitiveKind::Char | PrimitiveKind::WChar => Expr::Literal(Literal::Char('\0')),
404 PrimitiveKind::Float32 | PrimitiveKind::Float64 => {
405 Expr::Literal(Literal::Float("0".to_string()))
406 }
407 PrimitiveKind::Float128 => Expr::string("0"),
409 PrimitiveKind::Int64 | PrimitiveKind::UInt64 => Node::wide_signed(0),
410 PrimitiveKind::Octet
411 | PrimitiveKind::Int8
412 | PrimitiveKind::UInt8
413 | PrimitiveKind::Int16
414 | PrimitiveKind::UInt16
415 | PrimitiveKind::Int32
416 | PrimitiveKind::UInt32 => Expr::int(0),
417 }
418}
419
420fn selected_by_default(
422 ast: &Ast,
423 id: Id<TypeNode>,
424 selector: i128,
425) -> Result<Option<(Id<TypeNode>, String)>, EncodeError> {
426 Ok(selected_case(ast, id, selector)?
427 .map(|(member, member_type)| (member_type, member.name.clone())))
428}
429
430fn discriminant_literal(ast: &Ast, discriminator: Id<TypeNode>, value: i128) -> Expr {
435 let resolved = resolve_alias(ast, discriminator);
436 if matches!(
437 ast.type_(resolved).map(|node| node.kind.clone()),
438 Ok(TypeKind::Enumeration { .. })
439 ) {
440 if let Some(enumerator) = crate::emit::default_enumerator_name(ast, resolved) {
441 return Node::enumerator_constant(ast, resolved, &enumerator);
442 }
443 }
444 if matches!(
445 resolved_primitive(ast, resolved),
446 Some(PrimitiveKind::Boolean)
447 ) {
448 return Expr::bool(value != 0);
449 }
450 if matches!(
451 resolved_primitive(ast, resolved),
452 Some(PrimitiveKind::Char | PrimitiveKind::WChar)
453 ) {
454 let point = u32::try_from(value).ok().and_then(char::from_u32);
455 if let Some(ch) = point {
456 return Expr::Literal(Literal::Char(ch));
457 }
458 }
459 if matches!(
460 resolved_primitive(ast, resolved),
461 Some(PrimitiveKind::Int64 | PrimitiveKind::UInt64)
462 ) {
463 return Node::wide_signed(value);
464 }
465 Expr::int(value)
466}
467
468fn resolved_primitive(ast: &Ast, id: Id<TypeNode>) -> Option<PrimitiveKind> {
472 let mut current = id;
473 for _ in 0..MAX_ALIAS_DEPTH {
474 match ast.type_(current).map(|node| node.kind.clone()) {
475 Ok(TypeKind::Primitive(kind)) => return Some(kind),
476 Ok(TypeKind::Alias { aliased }) => current = aliased.resolved()?,
477 _ => return None,
478 }
479 }
480 None
481}
482
483#[allow(clippy::too_many_lines)]
485fn explicit(
486 ast: &Ast,
487 expr: Id<zerodds_ast::ConstExprNode>,
488 type_: Id<TypeNode>,
489 member_name: &str,
490) -> Result<Expr, EncodeError> {
491 let refuse = |kind: &'static str| EncodeError::NoWriter {
492 member: member_name.to_string(),
493 kind,
494 };
495 let node = ast
496 .expr(expr)
497 .map_err(|_| refuse("a `@default` this frontend cannot follow"))?;
498 let resolved = resolve_alias(ast, type_);
499 let kind = ast.type_(resolved).map(|node| node.kind.clone());
500 let is_float128 = matches!(kind, Ok(TypeKind::Primitive(PrimitiveKind::Float128)));
501 let is_wide = matches!(
502 kind,
503 Ok(TypeKind::Primitive(
504 PrimitiveKind::Int64 | PrimitiveKind::UInt64
505 ))
506 );
507 match &node.expr {
508 ConstExpr::Literal(AstLiteral::Boolean(flag)) => Ok(Expr::bool(*flag)),
509 ConstExpr::Literal(AstLiteral::Char(ch)) => Ok(Expr::Literal(Literal::Char(*ch))),
517 ConstExpr::Literal(AstLiteral::WChar(code)) => {
518 let point = char::from_u32(*code)
519 .ok_or_else(|| refuse("a `@default` `wchar` that is not a Unicode scalar value"))?;
520 Ok(Expr::Literal(Literal::Char(point)))
521 }
522 ConstExpr::Literal(AstLiteral::String(value)) => Ok(Expr::string(value)),
523 ConstExpr::Literal(AstLiteral::WString(codes)) => {
524 let mut out = String::new();
525 for code in codes {
526 let Some(ch) = char::from_u32(*code) else {
527 return Err(refuse("a `@default` wide string that is not code points"));
528 };
529 out.push(ch);
530 }
531 Ok(Expr::string(&out))
532 }
533 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
534 let exponent = exponent
535 .as_ref()
536 .map_or(String::new(), |text| format!("e{text}"));
537 let written = format!("{mantissa}{exponent}");
538 if is_float128 {
539 return Ok(Expr::string(&written));
544 }
545 Ok(Expr::Literal(Literal::Float(written)))
546 }
547 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => Ok(Expr::string(
548 &crate::adapter::constant::fixed_decimal(digits, *scale),
549 )),
550 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
551 let target = reference
552 .resolved()
553 .ok_or_else(|| refuse("a `@default` enumerator that is unresolved"))?;
554 let enumerator = ast
555 .enumerator(target)
556 .map_err(|_| refuse("a `@default` enumerator this frontend cannot follow"))?;
557 Ok(Node::enumerator_constant(ast, resolved, &enumerator.name))
558 }
559 _ => {
560 let value = ast
561 .eval_integral(expr)
562 .map_err(|_| refuse("a `@default` this frontend cannot evaluate"))?;
563 Ok(match kind {
564 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => Expr::bool(value != 0),
565 Ok(TypeKind::Primitive(PrimitiveKind::Char | PrimitiveKind::WChar)) => {
570 let point = u32::try_from(value)
571 .ok()
572 .filter(|code| *code <= u32::from(u16::MAX))
573 .and_then(char::from_u32)
574 .ok_or_else(|| {
575 refuse("a `@default` character outside the range of a UTF-16 code unit")
576 })?;
577 Expr::Literal(Literal::Char(point))
578 }
579 Ok(TypeKind::Primitive(PrimitiveKind::Float128)) => {
581 Expr::string(&format!("{value}"))
582 }
583 _ if is_wide => Node::wide_signed(value),
584 _ => Expr::int(value),
585 })
586 }
587 }
588}
589
590fn calls_in_stat(statement: &Stat, out: &mut Vec<String>) {
599 match statement {
600 Stat::Declare { value, .. } => {
601 if let Some(value) = value {
602 calls_in_expr(value, out);
603 }
604 }
605 Stat::Assign { place, value, .. } => {
606 calls_in_expr(place, out);
607 calls_in_expr(value, out);
608 }
609 Stat::Function(function) => {
610 for inner in &function.body {
611 calls_in_stat(inner, out);
612 }
613 }
614 Stat::Return(value) => {
615 if let Some(value) = value {
616 calls_in_expr(value, out);
617 }
618 }
619 Stat::Expr(value) | Stat::Throw(value) => calls_in_expr(value, out),
620 Stat::If {
621 condition,
622 then,
623 otherwise,
624 } => {
625 calls_in_expr(condition, out);
626 for inner in then {
627 calls_in_stat(inner, out);
628 }
629 if let Some(rest) = otherwise {
630 for inner in rest {
631 calls_in_stat(inner, out);
632 }
633 }
634 }
635 Stat::While { condition, body } => {
636 calls_in_expr(condition, out);
637 for inner in body {
638 calls_in_stat(inner, out);
639 }
640 }
641 Stat::For { bound, body, .. } => {
642 calls_in_expr(bound, out);
643 for inner in body {
644 calls_in_stat(inner, out);
645 }
646 }
647 Stat::Break | Stat::Comment(_) | Stat::Verbatim(_) => {}
648 }
649}
650
651fn calls_in_expr(expr: &Expr, out: &mut Vec<String>) {
656 match expr {
657 Expr::Call { callee, args } => {
658 if let Expr::Name(name) = callee.as_ref() {
661 if name.as_str().starts_with("zd_def_")
662 && !out.iter().any(|seen| seen == name.as_str())
663 {
664 out.push(name.as_str().to_string());
665 }
666 }
667 calls_in_expr(callee, out);
668 for arg in args {
669 calls_in_expr(arg, out);
670 }
671 }
672 Expr::Array(elements) => {
673 for element in elements {
674 calls_in_expr(element, out);
675 }
676 }
677 Expr::Object(fields) => {
678 for (_, value) in fields {
679 calls_in_expr(value, out);
680 }
681 }
682 Expr::New { args, .. } => {
683 for arg in args {
684 calls_in_expr(arg, out);
685 }
686 }
687 Expr::Member { object, .. } => calls_in_expr(object, out),
688 Expr::Index { object, index } => {
689 calls_in_expr(object, out);
690 calls_in_expr(index, out);
691 }
692 Expr::Paren(inner) | Expr::Unary { value: inner, .. } => calls_in_expr(inner, out),
693 Expr::Binary { lhs, rhs, .. } => {
694 calls_in_expr(lhs, out);
695 calls_in_expr(rhs, out);
696 }
697 Expr::Ternary {
698 condition,
699 then,
700 otherwise,
701 } => {
702 calls_in_expr(condition, out);
703 calls_in_expr(then, out);
704 calls_in_expr(otherwise, out);
705 }
706 Expr::Literal(_) | Expr::Undefined | Expr::Name(_) => {}
707 }
708}