1use crate::adapter::zig::{field_name, zig_type_name, zig_value_type};
33use crate::default_common::{discriminator_default_value, resolve_alias, selected_case};
34use crate::emit::zig::type_tag;
35use crate::encode::EncodeError;
36use crate::fixed_literal::{fixed_text, zero_decimal};
37use zerodds_ast::{
38 Ast, ConstExpr, Literal as AstLiteral, MemberNode, PrimitiveKind, TypeKind, TypeNode,
39};
40use zerodds_ast_core::Id;
41use zerodds_lang_cst::common::{Doc, Ident, Literal};
42use zerodds_lang_cst::zig;
43
44const MAX_ALIAS_DEPTH: usize = 64;
47
48const ALLOCATOR: &str = "zd_al";
50
51#[must_use]
55pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
56 match ast.type_(id).map(|node| node.kind.clone()) {
57 Ok(TypeKind::Primitive(kind)) => {
58 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
59 }
60 _ => format!("zd_def_{}", type_tag(ast, id, 0)),
61 }
62}
63
64#[must_use]
66pub fn defaulter_from_decoder(decoder: &str) -> String {
67 match decoder.strip_prefix("zd_dec_") {
68 Some(rest) => format!("zd_def_{rest}"),
69 None => format!("zd_def_{decoder}"),
70 }
71}
72
73pub fn member_default(
84 ast: &Ast,
85 node: &MemberNode,
86 type_: Id<TypeNode>,
87 place: zig::Expr,
88 alloc: zig::Expr,
89) -> Result<Vec<zig::Stmt>, EncodeError> {
90 let value = member_value(ast, node, type_, &alloc)?;
91 let external = crate::adapter::held_externally(ast, node);
92 let value = if external { boxed(alloc, value) } else { value };
93 Ok(vec![zig::Stmt::Assign { place, value }])
94}
95
96pub fn zig_defaulters_called_by(
102 ast: &Ast,
103 decls: &[zig::Decl],
104) -> Result<Vec<zig::Decl>, EncodeError> {
105 let index: Vec<(String, Id<TypeNode>)> = ast
106 .types()
107 .map(|(id, _)| (defaulter_name(ast, id), id))
108 .collect();
109 let mut called: Vec<String> = Vec::new();
110 for decl in decls {
111 calls_in_decl(decl, &mut called);
112 }
113 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
114 let mut have: Vec<String> = Vec::new();
115 for _ in 0..=index.len() {
116 let mut grew = false;
117 for name in &called {
118 if have.contains(name) {
119 continue;
120 }
121 have.push(name.clone());
122 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
127 wanted.push(id);
128 grew = true;
129 }
130 }
131 let produced = zig_defaulters_over(ast, &wanted)?;
132 if !grew {
133 return Ok(produced);
134 }
135 for decl in &produced {
136 calls_in_decl(decl, &mut called);
137 }
138 }
139 zig_defaulters_over(ast, &wanted)
140}
141
142pub fn zig_defaulters_over(
147 ast: &Ast,
148 types: &[Id<TypeNode>],
149) -> Result<Vec<zig::Decl>, EncodeError> {
150 let mut out = Vec::new();
151 let mut emitted: Vec<String> = Vec::new();
152 for id in types.iter().copied() {
153 let name = defaulter_name(ast, id);
154 if emitted.contains(&name) {
155 continue;
156 }
157 let body = match body_of(ast, id, &name)? {
158 Some(body) => body,
159 None => continue,
160 };
161 let returns = match zig_value_type(ast, resolve_alias(ast, id), &name) {
162 Ok(returns) => returns,
163 Err(_) => {
164 return Err(EncodeError::NoWriter {
165 member: name,
166 kind: "type with no Zig form to return",
167 });
168 }
169 };
170 emitted.push(name.clone());
171 let mut body = body;
172 if !body_mentions(&body, ALLOCATOR) {
177 body.insert(0, zig::Stmt::Discard(Ident::new(ALLOCATOR)));
178 }
179 out.push(zig::Decl::Func(zig::FuncDecl {
180 doc: Doc::line(
181 "Table 9 of DDS-XTypes 1.3: what this type is where the stream did not \
182 carry it.",
183 ),
184 is_pub: false,
185 name: Ident::new(&name),
186 params: vec![zig::FnParam {
187 name: Ident::new(ALLOCATOR),
188 type_: zig::Type::named("std.mem.Allocator"),
189 }],
190 returns: zig::Type::error_union(zig::Type::named("zdwire.WireError"), returns),
191 body,
192 }));
193 }
194 Ok(out)
195}
196
197fn body_mentions(body: &[zig::Stmt], name: &str) -> bool {
199 fn in_expr(expr: &zig::Expr, name: &str) -> bool {
204 match expr {
205 zig::Expr::Name(ident) => ident.as_str() == name,
206 zig::Expr::Binary { lhs, rhs, .. } => in_expr(lhs, name) || in_expr(rhs, name),
207 zig::Expr::Paren(value)
208 | zig::Expr::Unary { value, .. }
209 | zig::Expr::Try(value)
210 | zig::Expr::Dot { value, .. } => in_expr(value, name),
211 zig::Expr::Index { value, index } => in_expr(value, name) || in_expr(index, name),
212 zig::Expr::Call { func, args } => {
213 in_expr(func, name) || args.iter().any(|arg| in_expr(arg, name))
214 }
215 zig::Expr::StructLit(fields) => fields.iter().any(|(_, value)| in_expr(value, name)),
216 zig::Expr::ListLit(elements) => elements.iter().any(|element| in_expr(element, name)),
217 zig::Expr::Literal(_) | zig::Expr::Type(_) => false,
218 }
219 }
220 body.iter().any(|statement| match statement {
221 zig::Stmt::Return(Some(value)) | zig::Stmt::Expr(value) | zig::Stmt::DiscardExpr(value) => {
222 in_expr(value, name)
223 }
224 zig::Stmt::Assign { place, value } => in_expr(place, name) || in_expr(value, name),
225 zig::Stmt::Var { value, .. } => value.as_ref().is_some_and(|value| in_expr(value, name)),
226 _ => false,
227 })
228}
229
230#[allow(clippy::too_many_lines)]
233fn body_of(
234 ast: &Ast,
235 id: Id<TypeNode>,
236 own_name: &str,
237) -> Result<Option<Vec<zig::Stmt>>, EncodeError> {
238 let id = resolve_alias(ast, id);
241 let refuse = |kind: &'static str| EncodeError::NoWriter {
242 member: own_name.to_string(),
243 kind,
244 };
245 let Ok(node) = ast.type_(id) else {
258 return Err(refuse("a type the model does not hold"));
259 };
260 let give = |value: zig::Expr| Some(vec![zig::Stmt::Return(Some(value))]);
261 let alloc = || zig::Expr::Name(Ident::new(ALLOCATOR));
262 let body = match &node.kind {
263 TypeKind::Primitive(kind) => give(primitive_default(*kind)),
264
265 TypeKind::String { .. } => give(text("")),
267
268 TypeKind::Fixed { scale, .. } => give(text(&zero_decimal(*scale))),
270
271 TypeKind::Alias { .. } => {
272 return Err(refuse("typedef with an unresolved target"));
273 }
274
275 TypeKind::Enumeration { .. } => {
279 let enumerator = match crate::emit::default_enumerator_name(ast, id) {
280 Some(enumerator) => enumerator,
281 None => {
282 return Err(refuse(
283 "enumeration with no enumerator to take as its default",
284 ));
285 }
286 };
287 give(enumerator_expr(ast, id, &enumerator))
288 }
289
290 TypeKind::Bitmask { .. } => give(zig::Expr::int(0)),
293
294 TypeKind::Bitset { .. } => {
296 let mut fields = Vec::new();
297 for field in &ast.all_bitfields_of(id) {
298 let Ok(field_node) = ast.bitfield(*field) else {
299 continue;
300 };
301 let Some(field_ident) = field_node.name.as_ref() else {
302 continue;
303 };
304 fields.push((Ident::new(&field_name(field_ident)), zig::Expr::int(0)));
305 }
306 give(zig::Expr::StructLit(fields))
307 }
308
309 TypeKind::Sequence { .. } | TypeKind::Map { .. } => give(zig::Expr::Unary {
313 op: "&",
314 value: Box::new(zig::Expr::ListLit(Vec::new())),
315 }),
316
317 TypeKind::Array { element, dims } => {
323 let element = match element.resolved() {
324 Some(element) => element,
325 None => return Err(refuse("array with an unresolved element")),
326 };
327 let mut lengths = Vec::new();
328 for dim in dims {
329 let length = match ast.eval_integral(*dim) {
330 Ok(length) => length,
331 Err(_) => return Err(refuse("array whose length does not evaluate")),
332 };
333 let length = match usize::try_from(length) {
334 Ok(length) => length,
335 Err(_) => return Err(refuse("array of a length this column cannot spell")),
336 };
337 lengths.push(length);
338 }
339 let mut out = zig::Expr::try_(zig::Expr::call(
340 &defaulter_name(ast, element),
341 vec![alloc()],
342 ));
343 for length in lengths.into_iter().rev() {
344 out = zig::Expr::ListLit(
345 core::iter::repeat_with(|| out.clone())
346 .take(length)
347 .collect(),
348 );
349 }
350 give(out)
351 }
352
353 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
358 let mut fields = Vec::new();
359 if let Some(base) = crate::adapter::c::struct_base(ast, id) {
361 fields.push((
362 Ident::new(&field_name("parent")),
363 zig::Expr::try_(zig::Expr::call(&defaulter_name(ast, base), vec![alloc()])),
364 ));
365 }
366 for member_id in ast.members_of(id) {
367 let member_node = match ast.member(*member_id) {
368 Ok(member_node) => member_node,
369 Err(_) => continue,
370 };
371 if member_node.facets.is_optional() {
372 continue;
373 }
374 let member_type = match member_node.type_.resolved() {
375 Some(member_type) => member_type,
376 None => {
377 return Err(EncodeError::NoWriter {
378 member: member_node.name.clone(),
379 kind: "member with an unresolved type",
380 });
381 }
382 };
383 let mut value = member_value(ast, member_node, member_type, &alloc())?;
384 let external = crate::adapter::held_externally(ast, member_node);
385 if external {
386 value = boxed(alloc(), value);
387 }
388 fields.push((Ident::new(&field_name(&member_node.name)), value));
389 }
390 give(zig::Expr::StructLit(fields))
391 }
392
393 TypeKind::Union { discriminator, .. } => {
400 let discriminator = match discriminator.resolved() {
401 Some(discriminator) => discriminator,
402 None => return Err(refuse("union with an unresolved discriminator")),
403 };
404 let selector = discriminator_default_value(ast, discriminator);
405 let selected = selected_by_default(ast, id, selector)?;
406 let mut fields = vec![(
407 Ident::new("_d"),
408 discriminant_literal(ast, discriminator, selector.unwrap_or(0)),
409 )];
410 for case in ast.cases_of(id) {
411 let Ok(case_node) = ast.case(*case) else {
412 return Err(refuse("a case the model does not hold"));
413 };
414 let Ok(member) = ast.member(case_node.member) else {
415 return Err(refuse("a member the model does not hold"));
416 };
417 let field = field_name(&member.name);
418 let value = match &selected {
419 Some((member_type, selected_field)) if *selected_field == field => {
420 zig::Expr::try_(zig::Expr::call(
421 &defaulter_name(ast, *member_type),
422 vec![alloc()],
423 ))
424 }
425 _ => zig::Expr::Name(Ident::new("undefined")),
426 };
427 fields.push((Ident::new(&field), value));
428 }
429 give(zig::Expr::StructLit(fields))
430 }
431
432 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => None,
433 TypeKind::ValueBox { .. } => {
434 return Err(refuse(
435 "boxed value, whose presence flag has no settled wire form",
436 ));
437 }
438 TypeKind::Any => {
439 return Err(refuse(
440 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
441 as a TypeCode ahead of the value",
442 ));
443 }
444 TypeKind::ValueBase => {
445 return Err(refuse(
446 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
447 one, as a chunked value with its repository id",
448 ));
449 }
450 };
451 Ok(body)
452}
453
454fn boxed(alloc: zig::Expr, value: zig::Expr) -> zig::Expr {
457 zig::Expr::try_(zig::Expr::Call {
458 func: Box::new(zig::Expr::dot(
459 zig::Expr::Name(Ident::new("zdwire")),
460 "zdPtr",
461 )),
462 args: vec![alloc, value],
463 })
464}
465
466fn member_value(
469 ast: &Ast,
470 node: &MemberNode,
471 type_: Id<TypeNode>,
472 alloc: &zig::Expr,
473) -> Result<zig::Expr, EncodeError> {
474 match node.facets.xtypes.as_ref().and_then(|x| x.default_value) {
475 Some(expr) => explicit(ast, expr, type_, &node.name),
476 None => Ok(zig::Expr::try_(zig::Expr::call(
477 &defaulter_name(ast, type_),
478 vec![alloc.clone()],
479 ))),
480 }
481}
482
483fn primitive_default(kind: PrimitiveKind) -> zig::Expr {
488 match kind {
489 PrimitiveKind::Boolean => zig::Expr::Literal(Literal::Bool(false)),
490 PrimitiveKind::Float32 | PrimitiveKind::Float64 | PrimitiveKind::Float128 => {
491 zig::Expr::Literal(Literal::Float("0.0".to_string()))
492 }
493 PrimitiveKind::Char
494 | PrimitiveKind::WChar
495 | PrimitiveKind::Octet
496 | PrimitiveKind::Int8
497 | PrimitiveKind::UInt8
498 | PrimitiveKind::Int16
499 | PrimitiveKind::UInt16
500 | PrimitiveKind::Int32
501 | PrimitiveKind::UInt32
502 | PrimitiveKind::Int64
503 | PrimitiveKind::UInt64 => zig::Expr::int(0),
504 }
505}
506
507fn enumerator_expr(ast: &Ast, id: Id<TypeNode>, enumerator: &str) -> zig::Expr {
509 zig::Expr::dot(
510 zig::Expr::Name(Ident::new(&zig_type_name(ast, id))),
511 &field_name(enumerator),
512 )
513}
514
515fn selected_by_default(
517 ast: &Ast,
518 id: Id<TypeNode>,
519 selector: Option<i128>,
520) -> Result<Option<(Id<TypeNode>, String)>, EncodeError> {
521 let selector = match selector {
522 Some(selector) => selector,
523 None => return Ok(None),
524 };
525 Ok(selected_case(ast, id, selector)?
526 .map(|(member, member_type)| (member_type, field_name(&member.name))))
527}
528
529fn discriminant_literal(ast: &Ast, discriminator: Id<TypeNode>, value: i128) -> zig::Expr {
534 let resolved = resolve_alias(ast, discriminator);
535 if matches!(
536 ast.type_(resolved).map(|node| node.kind.clone()),
537 Ok(TypeKind::Enumeration { .. })
538 ) {
539 if let Some(enumerator) = crate::emit::default_enumerator_name(ast, resolved) {
540 return enumerator_expr(ast, resolved, &enumerator);
541 }
542 }
543 match resolved_primitive(ast, resolved) {
544 Some(PrimitiveKind::Boolean) => zig::Expr::Literal(Literal::Bool(value != 0)),
545 _ => zig::Expr::int(value),
546 }
547}
548
549fn resolved_primitive(ast: &Ast, id: Id<TypeNode>) -> Option<PrimitiveKind> {
551 let mut current = id;
552 for _ in 0..MAX_ALIAS_DEPTH {
553 match ast.type_(current).map(|node| node.kind.clone()) {
554 Ok(TypeKind::Primitive(kind)) => return Some(kind),
555 Ok(TypeKind::Alias { aliased }) => current = aliased.resolved()?,
556 _ => return None,
557 }
558 }
559 None
560}
561
562#[allow(clippy::too_many_lines)]
564fn explicit(
565 ast: &Ast,
566 expr: Id<zerodds_ast::ConstExprNode>,
567 type_: Id<TypeNode>,
568 member_name: &str,
569) -> Result<zig::Expr, EncodeError> {
570 let refuse = |kind: &'static str| EncodeError::NoWriter {
571 member: member_name.to_string(),
572 kind,
573 };
574 let node = match ast.expr(expr) {
575 Ok(node) => node,
576 Err(_) => return Err(refuse("a `@default` this frontend cannot follow")),
577 };
578 let resolved = resolve_alias(ast, type_);
579 let kind = ast.type_(resolved).map(|node| node.kind.clone());
580 match &node.expr {
581 ConstExpr::Literal(AstLiteral::Boolean(flag)) => {
582 Ok(zig::Expr::Literal(Literal::Bool(*flag)))
583 }
584 ConstExpr::Literal(AstLiteral::Char(ch)) => Ok(zig::Expr::int(i128::from(u32::from(*ch)))),
587 ConstExpr::Literal(AstLiteral::WChar(code)) => Ok(zig::Expr::int(i128::from(*code))),
588 ConstExpr::Literal(AstLiteral::String(value)) => Ok(text(value)),
589 ConstExpr::Literal(AstLiteral::WString(codes)) => {
590 let mut out = String::new();
591 for code in codes {
592 let ch = match char::from_u32(*code) {
593 Some(ch) => ch,
594 None => {
595 return Err(refuse("a `@default` wide string that is not code points"));
596 }
597 };
598 out.push(ch);
599 }
600 Ok(text(&out))
601 }
602 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
606 let exponent = match exponent.as_ref() {
607 Some(exponent) => format!("e{exponent}"),
608 None => String::new(),
609 };
610 Ok(zig::Expr::Literal(Literal::Float(format!(
611 "{mantissa}{exponent}"
612 ))))
613 }
614 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => {
615 Ok(text(&fixed_text(digits, *scale)))
616 }
617 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
618 let target = match reference.resolved() {
619 Some(target) => target,
620 None => return Err(refuse("a `@default` enumerator that is unresolved")),
621 };
622 let enumerator = match ast.enumerator(target) {
623 Ok(enumerator) => enumerator,
624 Err(_) => {
625 return Err(refuse(
626 "a `@default` enumerator this frontend cannot follow",
627 ));
628 }
629 };
630 Ok(enumerator_expr(ast, resolved, &enumerator.name))
631 }
632 _ => {
633 let value = match ast.eval_integral(expr) {
634 Ok(value) => value,
635 Err(_) => return Err(refuse("a `@default` this frontend cannot evaluate")),
636 };
637 Ok(match kind {
638 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => {
639 zig::Expr::Literal(Literal::Bool(value != 0))
640 }
641 Ok(TypeKind::Primitive(PrimitiveKind::Char)) => {
642 if !(0..=0xFF).contains(&value) {
643 return Err(refuse("a `@default` character outside one octet"));
644 }
645 zig::Expr::int(value)
646 }
647 Ok(TypeKind::Primitive(PrimitiveKind::WChar)) => {
648 if !(0..=0xFFFF).contains(&value) {
649 return Err(refuse(
650 "a `@default` character outside the range of a UTF-16 code unit",
651 ));
652 }
653 zig::Expr::int(value)
654 }
655 _ => zig::Expr::int(value),
656 })
657 }
658 }
659}
660
661fn calls_in_decl(decl: &zig::Decl, out: &mut Vec<String>) {
665 match decl {
666 zig::Decl::Func(func) => calls_in_body(&func.body, out),
667 zig::Decl::Const(constant) => calls_in_expr(&constant.value, out),
668 zig::Decl::Comment(_) | zig::Decl::Verbatim(_) | zig::Decl::AnalyseAll(_) => {}
669 }
670}
671
672fn calls_in_body(body: &[zig::Stmt], out: &mut Vec<String>) {
677 for statement in body {
678 calls_in_stmt(statement, out);
679 }
680}
681
682fn calls_in_stmt(statement: &zig::Stmt, out: &mut Vec<String>) {
687 match statement {
688 zig::Stmt::Var { value, .. } => {
689 if let Some(value) = value {
690 calls_in_expr(value, out);
691 }
692 }
693 zig::Stmt::Assign { place, value } => {
694 calls_in_expr(place, out);
695 calls_in_expr(value, out);
696 }
697 zig::Stmt::Expr(value) | zig::Stmt::DiscardExpr(value) => calls_in_expr(value, out),
698 zig::Stmt::Return(value) => {
699 if let Some(value) = value {
700 calls_in_expr(value, out);
701 }
702 }
703 zig::Stmt::If {
704 condition,
705 then,
706 otherwise,
707 } => {
708 calls_in_expr(condition, out);
709 calls_in_body(then, out);
710 if let Some(otherwise) = otherwise {
711 calls_in_body(otherwise, out);
712 }
713 }
714 zig::Stmt::CountUp { bound, body, .. } => {
715 calls_in_expr(bound, out);
716 calls_in_body(body, out);
717 }
718 zig::Stmt::While { condition, body } => {
719 calls_in_expr(condition, out);
720 calls_in_body(body, out);
721 }
722 zig::Stmt::Discard(_) | zig::Stmt::Break | zig::Stmt::Comment(_) => {}
723 }
724}
725
726fn calls_in_expr(expr: &zig::Expr, out: &mut Vec<String>) {
731 match expr {
732 zig::Expr::Call { func, args } => {
733 if let zig::Expr::Name(name) = func.as_ref() {
738 let name = name.as_str();
739 if name.starts_with("zd_def_") && !out.iter().any(|seen| seen == name) {
740 out.push(name.to_string());
741 }
742 }
743 calls_in_expr(func, out);
744 for arg in args {
745 calls_in_expr(arg, out);
746 }
747 }
748 zig::Expr::Binary { lhs, rhs, .. } => {
749 calls_in_expr(lhs, out);
750 calls_in_expr(rhs, out);
751 }
752 zig::Expr::Paren(value)
753 | zig::Expr::Unary { value, .. }
754 | zig::Expr::Try(value)
755 | zig::Expr::Dot { value, .. } => calls_in_expr(value, out),
756 zig::Expr::Index { value, index } => {
757 calls_in_expr(value, out);
758 calls_in_expr(index, out);
759 }
760 zig::Expr::StructLit(fields) => {
761 for (_, value) in fields {
762 calls_in_expr(value, out);
763 }
764 }
765 zig::Expr::ListLit(elements) => {
766 for element in elements {
767 calls_in_expr(element, out);
768 }
769 }
770 zig::Expr::Literal(_) | zig::Expr::Name(_) | zig::Expr::Type(_) => {}
771 }
772}
773
774fn text(value: &str) -> zig::Expr {
777 zig::Expr::Literal(Literal::Str(value.to_string()))
778}