1use crate::adapter::elixir::type_module_path;
47use crate::default_common::{discriminator_default_value, resolve_alias, selected_case};
48use crate::emit::CodecTarget;
49use crate::emit::elixir::Elixir;
50use crate::encode::EncodeError;
51use crate::fixed_literal::{fixed_text, zero_decimal};
52use zerodds_ast::{
53 Ast, ConstExpr, Literal as AstLiteral, MemberNode, PrimitiveKind, TypeKind, TypeNode,
54};
55use zerodds_ast_core::Id;
56use zerodds_lang_cst::common::{Doc, Ident, Literal, Path};
57use zerodds_lang_cst::elixir::{self as ex, atom_name};
58
59const MAX_ALIAS_DEPTH: usize = 64;
62
63#[must_use]
66pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
67 match ast.type_(id).map(|node| node.kind.clone()) {
68 Ok(TypeKind::Primitive(kind)) => {
69 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
70 }
71 _ => format!("zd_def_{}", Elixir::type_name(ast, id)),
72 }
73}
74
75#[must_use]
78pub fn defaulter_from_decoder(decoder: &str) -> String {
79 match decoder.strip_prefix("zd_dec_") {
80 Some(rest) => format!("zd_def_{rest}"),
81 None => format!("zd_def_{decoder}"),
82 }
83}
84
85#[must_use]
87pub fn default_into(ast: &Ast, id: Id<TypeNode>, place: ex::Expr) -> ex::Statement {
88 assign(place, call(&defaulter_name(ast, id), Vec::new()))
89}
90
91pub fn member_default(
99 ast: &Ast,
100 node: &MemberNode,
101 type_: Id<TypeNode>,
102 place: ex::Expr,
103) -> Result<Vec<ex::Statement>, EncodeError> {
104 Ok(vec![assign(place, member_value(ast, node, type_)?)])
105}
106
107pub fn elixir_defaulters_called_by(
115 ast: &Ast,
116 decls: &[ex::CodecFunction],
117) -> Result<Vec<ex::CodecFunction>, EncodeError> {
118 let index: Vec<(String, Id<TypeNode>)> = ast
119 .types()
120 .map(|(id, _)| (defaulter_name(ast, id), id))
121 .collect();
122 let mut called: Vec<String> = Vec::new();
123 for decl in decls {
124 calls_in_function(decl, &mut called);
125 }
126 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
127 let mut have: Vec<String> = Vec::new();
128 for _ in 0..=index.len() {
131 let mut grew = false;
132 for name in &called.clone() {
133 if have.contains(name) {
134 continue;
135 }
136 have.push(name.clone());
137 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
138 wanted.push(id);
139 grew = true;
140 }
141 }
142 let produced = elixir_defaulters_over(ast, &wanted)?;
143 if !grew {
144 return Ok(produced);
145 }
146 for decl in &produced {
147 calls_in_function(decl, &mut called);
148 }
149 }
150 elixir_defaulters_over(ast, &wanted)
151}
152
153pub fn elixir_defaulters_over(
158 ast: &Ast,
159 types: &[Id<TypeNode>],
160) -> Result<Vec<ex::CodecFunction>, EncodeError> {
161 let mut out = Vec::new();
162 let mut emitted: Vec<String> = Vec::new();
163 for id in types.iter().copied() {
164 let name = defaulter_name(ast, id);
165 if emitted.contains(&name) {
166 continue;
167 }
168 let Some(value) = value_of(ast, id, &name)? else {
169 continue;
170 };
171 emitted.push(name.clone());
172 out.push(ex::CodecFunction {
173 doc: Doc::line(
174 "Table 9 of DDS-XTypes 1.3: what this type is where the stream did not carry it.",
175 ),
176 name: Ident::new(&name),
177 params: Vec::new(),
178 body: Vec::new(),
179 result: value,
180 });
181 }
182 Ok(out)
183}
184
185#[allow(clippy::too_many_lines)]
188fn value_of(ast: &Ast, id: Id<TypeNode>, own_name: &str) -> Result<Option<ex::Expr>, EncodeError> {
189 let id = resolve_alias(ast, id);
193 let refuse = |kind: &'static str| EncodeError::NoWriter {
194 member: own_name.to_string(),
195 kind,
196 };
197 let Ok(node) = ast.type_(id) else {
205 return Err(refuse("a type the model does not hold"));
206 };
207 let value = match &node.kind {
208 TypeKind::Primitive(kind) => primitive_default(*kind),
209
210 TypeKind::String { .. } => text(""),
212
213 TypeKind::Fixed { scale, .. } => text(&zero_decimal(*scale)),
215
216 TypeKind::Alias { .. } => {
217 return Err(refuse("typedef with an unresolved target"));
218 }
219
220 TypeKind::Enumeration { .. } => {
222 let Some(enumerator) = crate::emit::default_enumerator_name(ast, id) else {
223 return Err(refuse(
224 "enumeration with no enumerator to take as its default",
225 ));
226 };
227 Elixir::enumerator_constant(ast, id, &enumerator)
228 }
229
230 TypeKind::Bitmask { .. } => ex::Expr::int(0),
232
233 TypeKind::Bitset { .. } => {
235 let mut fields = Vec::new();
236 for field in &ast.all_bitfields_of(id) {
237 let Ok(field_node) = ast.bitfield(*field) else {
238 continue;
239 };
240 if let Some(field_name) = field_node.name.as_ref() {
241 fields.push((Ident::new(&atom_name(field_name)), ex::Expr::int(0)));
242 }
243 }
244 ex::Expr::StructLiteral {
245 module: type_module_path(ast, id),
246 fields,
247 }
248 }
249
250 TypeKind::Sequence { .. } | TypeKind::Map { .. } => ex::Expr::List(Vec::new()),
254
255 TypeKind::Array { element, dims } => {
258 let Some(element) = element.resolved() else {
259 return Err(refuse("array with an unresolved element"));
260 };
261 let mut lengths = Vec::new();
262 for dim in dims {
263 let length = ast
264 .eval_integral(*dim)
265 .map_err(|_| refuse("array whose length does not evaluate"))?;
266 lengths.push(
267 usize::try_from(length)
268 .map_err(|_| refuse("array of a length this column cannot spell"))?,
269 );
270 }
271 let mut out = call(&defaulter_name(ast, element), Vec::new());
272 for length in lengths.into_iter().rev() {
273 out = ex::Expr::Tuple(vec![out; length]);
274 }
275 out
276 }
277
278 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
281 let mut fields = Vec::new();
282 if let Some(base) = crate::adapter::c::struct_base(ast, id) {
284 fields.push((
285 Ident::new(&atom_name("parent")),
286 call(&defaulter_name(ast, base), Vec::new()),
287 ));
288 }
289 for member_id in ast.members_of(id) {
290 let Ok(member_node) = ast.member(*member_id) else {
291 continue;
292 };
293 if member_node.facets.is_optional() {
294 continue;
295 }
296 let Some(member_type) = member_node.type_.resolved() else {
297 return Err(EncodeError::NoWriter {
298 member: member_node.name.clone(),
299 kind: "member with an unresolved type",
300 });
301 };
302 fields.push((
303 Ident::new(&atom_name(&member_node.name)),
304 member_value(ast, member_node, member_type)?,
305 ));
306 }
307 ex::Expr::StructLiteral {
308 module: type_module_path(ast, id),
309 fields,
310 }
311 }
312
313 TypeKind::Union { discriminator, .. } => {
317 let Some(discriminator) = discriminator.resolved() else {
318 return Err(refuse("union with an unresolved discriminator"));
319 };
320 let selector = discriminator_default_value(ast, discriminator);
321 let mut fields = vec![(
322 Ident::new("_d"),
323 discriminant_literal(ast, discriminator, selector.unwrap_or(0)),
324 )];
325 if let Some((member_type, field)) = selected_by_default(ast, id, selector)? {
326 fields.push((
327 Ident::new(&field),
328 call(&defaulter_name(ast, member_type), Vec::new()),
329 ));
330 }
331 ex::Expr::StructLiteral {
332 module: type_module_path(ast, id),
333 fields,
334 }
335 }
336
337 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
338 return Ok(None);
339 }
340 TypeKind::ValueBox { .. } => {
341 return Err(refuse(
342 "boxed value, whose presence flag has no settled wire form",
343 ));
344 }
345 TypeKind::Any => {
346 return Err(refuse(
347 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
348 as a TypeCode ahead of the value",
349 ));
350 }
351 TypeKind::ValueBase => {
352 return Err(refuse(
353 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
354 one, as a chunked value with its repository id",
355 ));
356 }
357 };
358 Ok(Some(value))
359}
360
361fn member_value(
364 ast: &Ast,
365 node: &MemberNode,
366 type_: Id<TypeNode>,
367) -> Result<ex::Expr, EncodeError> {
368 match node.facets.xtypes.as_ref().and_then(|x| x.default_value) {
369 Some(expr) => explicit(ast, expr, type_, &node.name),
370 None => Ok(call(&defaulter_name(ast, type_), Vec::new())),
371 }
372}
373
374fn primitive_default(kind: PrimitiveKind) -> ex::Expr {
376 match kind {
377 PrimitiveKind::Boolean => ex::Expr::Literal(Literal::Bool(false)),
378 PrimitiveKind::Char | PrimitiveKind::WChar => text("\0"),
380 PrimitiveKind::Float32 | PrimitiveKind::Float64 => {
383 ex::Expr::Literal(Literal::Float("0.0".to_string()))
384 }
385 PrimitiveKind::Float128 => ex::Expr::Call {
387 func: Path::new(&["ZeroddsWire", "Float128", "zero"]),
388 args: Vec::new(),
389 },
390 PrimitiveKind::Octet
391 | PrimitiveKind::Int8
392 | PrimitiveKind::UInt8
393 | PrimitiveKind::Int16
394 | PrimitiveKind::UInt16
395 | PrimitiveKind::Int32
396 | PrimitiveKind::UInt32
397 | PrimitiveKind::Int64
398 | PrimitiveKind::UInt64 => ex::Expr::int(0),
399 }
400}
401
402fn selected_by_default(
404 ast: &Ast,
405 id: Id<TypeNode>,
406 selector: Option<i128>,
407) -> Result<Option<(Id<TypeNode>, String)>, EncodeError> {
408 let Some(selector) = selector else {
409 return Ok(None);
410 };
411 Ok(selected_case(ast, id, selector)?
412 .map(|(member, member_type)| (member_type, atom_name(&member.name))))
413}
414
415fn discriminant_literal(ast: &Ast, discriminator: Id<TypeNode>, value: i128) -> ex::Expr {
418 let resolved = resolve_alias(ast, discriminator);
419 if matches!(
420 ast.type_(resolved).map(|node| node.kind.clone()),
421 Ok(TypeKind::Enumeration { .. })
422 ) {
423 if let Some(enumerator) = crate::emit::default_enumerator_name(ast, resolved) {
424 return Elixir::enumerator_constant(ast, resolved, &enumerator);
425 }
426 }
427 match resolved_primitive(ast, resolved) {
428 Some(PrimitiveKind::Boolean) => ex::Expr::Literal(Literal::Bool(value != 0)),
429 Some(PrimitiveKind::Char | PrimitiveKind::WChar) => {
432 char_literal(u32::try_from(value).unwrap_or(0))
433 }
434 _ => ex::Expr::int(value),
435 }
436}
437
438fn resolved_primitive(ast: &Ast, id: Id<TypeNode>) -> Option<PrimitiveKind> {
441 let mut current = id;
442 for _ in 0..MAX_ALIAS_DEPTH {
443 match ast.type_(current).map(|node| node.kind.clone()) {
444 Ok(TypeKind::Primitive(kind)) => return Some(kind),
445 Ok(TypeKind::Alias { aliased }) => current = aliased.resolved()?,
446 _ => return None,
447 }
448 }
449 None
450}
451
452#[allow(clippy::too_many_lines)]
454fn explicit(
455 ast: &Ast,
456 expr: Id<zerodds_ast::ConstExprNode>,
457 type_: Id<TypeNode>,
458 member_name: &str,
459) -> Result<ex::Expr, EncodeError> {
460 let refuse = |kind: &'static str| EncodeError::NoWriter {
461 member: member_name.to_string(),
462 kind,
463 };
464 let node = ast
465 .expr(expr)
466 .map_err(|_| refuse("a `@default` this frontend cannot follow"))?;
467 let resolved = resolve_alias(ast, type_);
468 let kind = ast.type_(resolved).map(|node| node.kind.clone());
469 let is_float128 = matches!(kind, Ok(TypeKind::Primitive(PrimitiveKind::Float128)));
470 match &node.expr {
471 ConstExpr::Literal(AstLiteral::Boolean(flag)) => {
472 Ok(ex::Expr::Literal(Literal::Bool(*flag)))
473 }
474 ConstExpr::Literal(AstLiteral::Char(ch)) => Ok(char_literal(u32::from(*ch))),
475 ConstExpr::Literal(AstLiteral::WChar(code)) => Ok(char_literal(*code)),
476 ConstExpr::Literal(AstLiteral::String(value)) => Ok(text(value)),
477 ConstExpr::Literal(AstLiteral::WString(codes)) => {
478 let mut out = String::new();
479 for code in codes {
480 let Some(ch) = char::from_u32(*code) else {
481 return Err(refuse("a `@default` wide string that is not code points"));
482 };
483 out.push(ch);
484 }
485 Ok(text(&out))
486 }
487 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
488 let exponent = exponent
489 .as_ref()
490 .map_or(String::new(), |text| format!("e{text}"));
491 let written = format!("{mantissa}{exponent}");
492 if is_float128 {
493 return Ok(ex::Expr::Call {
496 func: Path::new(&["ZeroddsWire", "Float128", "from_decimal"]),
497 args: vec![text(&written)],
498 });
499 }
500 Ok(ex::Expr::Literal(Literal::Float(written)))
503 }
504 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => {
505 Ok(text(&fixed_text(digits, *scale)))
506 }
507 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
508 let target = reference
509 .resolved()
510 .ok_or_else(|| refuse("a `@default` enumerator that is unresolved"))?;
511 let enumerator = ast
512 .enumerator(target)
513 .map_err(|_| refuse("a `@default` enumerator this frontend cannot follow"))?;
514 Ok(Elixir::enumerator_constant(ast, resolved, &enumerator.name))
515 }
516 _ => {
517 let value = ast
518 .eval_integral(expr)
519 .map_err(|_| refuse("a `@default` this frontend cannot evaluate"))?;
520 Ok(match kind {
521 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => {
522 ex::Expr::Literal(Literal::Bool(value != 0))
523 }
524 Ok(TypeKind::Primitive(PrimitiveKind::Char | PrimitiveKind::WChar)) => {
530 let point = u32::try_from(value)
531 .ok()
532 .filter(|code| *code <= u32::from(u16::MAX))
533 .ok_or_else(|| {
534 refuse("a `@default` character outside the range of a UTF-16 code unit")
535 })?;
536 if char::from_u32(point).is_none() {
537 return Err(refuse(
538 "a `@default` surrogate, which no Elixir binary can hold",
539 ));
540 }
541 char_literal(point)
542 }
543 Ok(TypeKind::Primitive(PrimitiveKind::Float128)) => ex::Expr::Call {
544 func: Path::new(&["ZeroddsWire", "Float128", "from_int"]),
545 args: vec![ex::Expr::int(value)],
546 },
547 _ => ex::Expr::int(value),
548 })
549 }
550 }
551}
552
553fn char_literal(code: u32) -> ex::Expr {
559 match char::from_u32(code) {
560 Some(point) => ex::Expr::Literal(Literal::Str(point.to_string())),
561 None => text("\0"),
562 }
563}
564
565fn calls_in_function(function: &ex::CodecFunction, out: &mut Vec<String>) {
569 calls_in_statements(&function.body, out);
570 calls_in_expr(&function.result, out);
571}
572
573fn calls_in_statements(statements: &[ex::Statement], out: &mut Vec<String>) {
578 for statement in statements {
579 match statement {
580 ex::Statement::Bind { value, .. } | ex::Statement::BindTuple { value, .. } => {
581 calls_in_expr(value, out);
582 }
583 ex::Statement::Update { path, value, .. } => {
584 for step in path {
585 if let ex::Access::At(index) = step {
586 calls_in_expr(index, out);
587 }
588 }
589 calls_in_expr(value, out);
590 }
591 ex::Statement::If {
592 condition,
593 then,
594 otherwise,
595 } => {
596 calls_in_expr(condition, out);
597 calls_in_statements(then, out);
598 calls_in_statements(otherwise, out);
599 }
600 ex::Statement::For { bound, body, .. } => {
601 calls_in_expr(bound, out);
602 calls_in_statements(body, out);
603 }
604 ex::Statement::While { condition, body } => {
605 calls_in_expr(condition, out);
606 calls_in_statements(body, out);
607 }
608 ex::Statement::Return(value) => calls_in_expr(value, out),
609 ex::Statement::Seq(inner) => calls_in_statements(inner, out),
610 ex::Statement::Break | ex::Statement::Comment(_) => {}
611 }
612 }
613}
614
615fn calls_in_expr(expr: &ex::Expr, out: &mut Vec<String>) {
620 match expr {
621 ex::Expr::Call { func, args } => {
622 if func.segments().len() == 1 {
623 let name = func
624 .segments()
625 .first()
626 .map(|segment| segment.as_str().to_string())
627 .unwrap_or_default();
628 if name.starts_with("zd_def_") && !out.iter().any(|seen| seen == &name) {
629 out.push(name);
630 }
631 }
632 for argument in args {
633 calls_in_expr(argument, out);
634 }
635 }
636 ex::Expr::List(items) | ex::Expr::Tuple(items) => {
637 for item in items {
638 calls_in_expr(item, out);
639 }
640 }
641 ex::Expr::Keyword(pairs) => {
642 for (_, value) in pairs {
643 calls_in_expr(value, out);
644 }
645 }
646 ex::Expr::StructLiteral { fields, .. } => {
647 for (_, value) in fields {
648 calls_in_expr(value, out);
649 }
650 }
651 ex::Expr::Binary { lhs, rhs, .. } => {
652 calls_in_expr(lhs, out);
653 calls_in_expr(rhs, out);
654 }
655 ex::Expr::Unary { value, .. } | ex::Expr::Paren(value) => calls_in_expr(value, out),
656 ex::Expr::Field { value, .. } => calls_in_expr(value, out),
657 ex::Expr::Index { value, index } => {
658 calls_in_expr(value, out);
659 calls_in_expr(index, out);
660 }
661 ex::Expr::IfElse {
662 condition,
663 then,
664 otherwise,
665 } => {
666 calls_in_expr(condition, out);
667 calls_in_expr(then, out);
668 calls_in_expr(otherwise, out);
669 }
670 ex::Expr::Literal(_)
671 | ex::Expr::Atom(_)
672 | ex::Expr::Var(_)
673 | ex::Expr::Attr(_)
674 | ex::Expr::Module(_) => {}
675 }
676}
677
678fn text(value: &str) -> ex::Expr {
681 ex::Expr::Literal(Literal::Str(value.to_string()))
682}
683
684fn call(callee: &str, args: Vec<ex::Expr>) -> ex::Expr {
685 ex::Expr::Call {
686 func: Path::single(callee),
687 args,
688 }
689}
690
691fn assign(place: ex::Expr, value: ex::Expr) -> ex::Statement {
692 match place_path(&place) {
693 Some((base, path)) if path.is_empty() => ex::Statement::Bind { name: base, value },
694 Some((base, path)) => ex::Statement::Update { base, path, value },
695 None => ex::Statement::Bind {
700 name: Ident::new("_"),
701 value: ex::Expr::Call {
702 func: Path::single("zd_refused_not_a_place"),
703 args: vec![place, value],
704 },
705 },
706 }
707}
708
709fn place_path(place: &ex::Expr) -> Option<(Ident, Vec<ex::Access>)> {
717 match place {
718 ex::Expr::Var(name) => Some((name.clone(), Vec::new())),
719 ex::Expr::Field { value, name } => {
720 let (base, mut path) = place_path(value)?;
721 path.push(ex::Access::Key(name.clone()));
722 Some((base, path))
723 }
724 ex::Expr::Index { value, index } => {
725 let (base, mut path) = place_path(value)?;
726 path.push(ex::Access::At((**index).clone()));
727 Some((base, path))
728 }
729 _ => None,
730 }
731}