1use crate::default_common::{discriminator_default_value, resolve_alias, selected_case};
47use crate::emit::CodecTarget;
48use crate::emit::lua::{CODECS, Lua};
49use crate::encode::EncodeError;
50use crate::fixed_literal::zero_decimal;
51use zerodds_ast::{
52 Ast, ConstExpr, Literal as AstLiteral, MemberNode, PrimitiveKind, TypeKind, TypeNode,
53};
54use zerodds_ast_core::Id;
55use zerodds_lang_cst::common::{Doc, Literal};
56use zerodds_lang_cst::lua::{self, Entry, Expr, Place, Stat};
57
58const MAX_ALIAS_DEPTH: usize = 64;
61
62#[must_use]
67pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
68 match ast.type_(id).map(|node| node.kind.clone()) {
69 Ok(TypeKind::Primitive(kind)) => {
70 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
71 }
72 _ => format!("zd_def_{}", Lua::type_name(ast, id)),
73 }
74}
75
76#[must_use]
79pub fn defaulter_from_decoder(decoder: &str) -> String {
80 match decoder.strip_prefix("zd_dec_") {
81 Some(rest) => format!("zd_def_{rest}"),
82 None => format!("zd_def_{decoder}"),
83 }
84}
85
86#[must_use]
88pub fn default_into(ast: &Ast, id: Id<TypeNode>, place: Expr) -> Stat {
89 Stat::Assign {
90 doc: Doc::none(),
91 target: place_of(place),
92 value: codec_call(&defaulter_name(ast, id), Vec::new()),
93 }
94}
95
96pub fn member_default(
104 ast: &Ast,
105 node: &MemberNode,
106 type_: Id<TypeNode>,
107 place: Expr,
108) -> Result<Vec<Stat>, EncodeError> {
109 Ok(vec![Stat::Assign {
110 doc: Doc::none(),
111 target: place_of(place),
112 value: member_value(ast, node, type_)?,
113 }])
114}
115
116pub fn lua_defaulters_called_by(ast: &Ast, decls: &[Stat]) -> Result<Vec<Stat>, EncodeError> {
123 let index: Vec<(String, Id<TypeNode>)> = ast
124 .types()
125 .map(|(id, _)| (defaulter_name(ast, id), id))
126 .collect();
127 let mut called: Vec<String> = Vec::new();
128 for decl in decls {
129 calls_in_stat(decl, &mut called);
130 }
131 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
132 let mut have: Vec<String> = Vec::new();
133 for _ in 0..=index.len() {
134 let mut grew = false;
135 for name in &called {
136 if have.contains(name) {
137 continue;
138 }
139 have.push(name.clone());
140 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
141 wanted.push(id);
142 grew = true;
143 }
144 }
145 let produced = lua_defaulters_over(ast, &wanted)?;
146 if !grew {
147 return Ok(produced);
148 }
149 for decl in &produced {
150 calls_in_stat(decl, &mut called);
151 }
152 }
153 lua_defaulters_over(ast, &wanted)
154}
155
156pub fn lua_defaulters_over(ast: &Ast, types: &[Id<TypeNode>]) -> Result<Vec<Stat>, 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(Stat::Function(lua::Function {
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 target: Place::name(CODECS).field(&name),
177 params: Vec::new(),
178 body: vec![Stat::Return(Some(value))],
179 }));
180 }
181 Ok(out)
182}
183
184#[allow(clippy::too_many_lines)]
187fn value_of(ast: &Ast, id: Id<TypeNode>, own_name: &str) -> Result<Option<Expr>, EncodeError> {
188 let id = resolve_alias(ast, id);
192 let refuse = |kind: &'static str| EncodeError::NoWriter {
193 member: own_name.to_string(),
194 kind,
195 };
196 let Ok(node) = ast.type_(id) else {
204 return Err(refuse("a type the model does not hold"));
205 };
206 let value = match &node.kind {
207 TypeKind::Primitive(kind) => primitive_default(*kind),
208
209 TypeKind::String { .. } => Expr::string(""),
211
212 TypeKind::Fixed { scale, .. } => Expr::string(&zero_decimal(*scale)),
214
215 TypeKind::Alias { .. } => {
216 return Err(refuse("typedef with an unresolved target"));
217 }
218
219 TypeKind::Enumeration { .. } => {
223 let Some(enumerator) = crate::emit::default_enumerator_name(ast, id) else {
224 return Err(refuse(
225 "enumeration with no enumerator to take as its default",
226 ));
227 };
228 Lua::enumerator_constant(ast, id, &enumerator)
229 }
230
231 TypeKind::Bitmask { .. } => Expr::int(0),
233
234 TypeKind::Bitset { .. } => {
237 let mut entries = Vec::new();
238 for field in &ast.all_bitfields_of(id) {
239 let Ok(field_node) = ast.bitfield(*field) else {
240 continue;
241 };
242 let Some(field_name) = field_node.name.as_ref() else {
244 continue;
245 };
246 entries.push(Entry {
247 doc: Doc::none(),
248 key: lua::table_key(field_name),
249 value: Expr::int(0),
250 });
251 }
252 Expr::Table(lua::Table::of(entries))
253 }
254
255 TypeKind::Sequence { .. } | TypeKind::Map { .. } => Expr::Table(lua::Table::default()),
260
261 TypeKind::Array { element, dims } => {
266 let Some(element) = element.resolved() else {
267 return Err(refuse("array with an unresolved element"));
268 };
269 let mut lengths = Vec::new();
270 for dim in dims {
271 let length = ast
272 .eval_integral(*dim)
273 .map_err(|_| refuse("array whose length does not evaluate"))?;
274 lengths.push(
275 usize::try_from(length)
276 .map_err(|_| refuse("array of a length this column cannot spell"))?,
277 );
278 }
279 let mut out = codec_call(&defaulter_name(ast, element), Vec::new());
280 for length in lengths.into_iter().rev() {
281 out = Expr::Table(lua::Table::of(
282 vec![out; length]
283 .into_iter()
284 .map(Entry::positional)
285 .collect(),
286 ));
287 }
288 out
289 }
290
291 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
294 let mut entries = Vec::new();
295 if let Some(base) = crate::adapter::c::struct_base(ast, id) {
297 entries.push(Entry {
298 doc: Doc::none(),
299 key: lua::table_key("parent"),
300 value: codec_call(&defaulter_name(ast, base), Vec::new()),
301 });
302 }
303 for member_id in ast.members_of(id) {
304 let Ok(member_node) = ast.member(*member_id) else {
305 continue;
306 };
307 if member_node.facets.is_optional() {
309 continue;
310 }
311 let Some(member_type) = member_node.type_.resolved() else {
312 return Err(EncodeError::NoWriter {
313 member: member_node.name.clone(),
314 kind: "member with an unresolved type",
315 });
316 };
317 entries.push(Entry {
318 doc: Doc::none(),
319 key: lua::table_key(&member_node.name),
320 value: member_value(ast, member_node, member_type)?,
321 });
322 }
323 Expr::Table(lua::Table::of(entries))
324 }
325
326 TypeKind::Union { discriminator, .. } => {
332 let Some(discriminator) = discriminator.resolved() else {
333 return Err(refuse("union with an unresolved discriminator"));
334 };
335 let selector = discriminator_default_value(ast, discriminator);
336 let mut entries = vec![Entry::named(
337 "_d",
338 discriminant_literal(ast, discriminator, selector.unwrap_or(0)),
339 )];
340 if let Some((member_type, field)) = selected_by_default(ast, id, selector)? {
341 entries.push(Entry {
342 doc: Doc::none(),
343 key: lua::table_key(&field),
344 value: codec_call(&defaulter_name(ast, member_type), Vec::new()),
345 });
346 }
347 Expr::Table(lua::Table::of(entries))
348 }
349
350 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
351 return Ok(None);
352 }
353 TypeKind::ValueBox { .. } => {
354 return Err(refuse(
355 "boxed value, whose presence flag has no settled wire form",
356 ));
357 }
358 TypeKind::Any => {
359 return Err(refuse(
360 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
361 as a TypeCode ahead of the value",
362 ));
363 }
364 TypeKind::ValueBase => {
365 return Err(refuse(
366 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
367 one, as a chunked value with its repository id",
368 ));
369 }
370 };
371 Ok(Some(value))
372}
373
374fn member_value(ast: &Ast, node: &MemberNode, type_: Id<TypeNode>) -> Result<Expr, EncodeError> {
377 match node.facets.xtypes.as_ref().and_then(|x| x.default_value) {
378 Some(expr) => explicit(ast, expr, type_, &node.name),
379 None => Ok(codec_call(&defaulter_name(ast, type_), Vec::new())),
380 }
381}
382
383fn primitive_default(kind: PrimitiveKind) -> Expr {
385 match kind {
386 PrimitiveKind::Boolean => Expr::bool(false),
387 PrimitiveKind::Char | PrimitiveKind::WChar => Expr::int(0),
389 PrimitiveKind::Float32 | PrimitiveKind::Float64 => {
392 Expr::Literal(Literal::Float(lua::float_text("0")))
393 }
394 PrimitiveKind::Float128 => Expr::string("0.0"),
397 PrimitiveKind::Octet
398 | PrimitiveKind::Int8
399 | PrimitiveKind::UInt8
400 | PrimitiveKind::Int16
401 | PrimitiveKind::UInt16
402 | PrimitiveKind::Int32
403 | PrimitiveKind::UInt32
404 | PrimitiveKind::Int64
405 | PrimitiveKind::UInt64 => Expr::int(0),
406 }
407}
408
409fn selected_by_default(
411 ast: &Ast,
412 id: Id<TypeNode>,
413 selector: Option<i128>,
414) -> Result<Option<(Id<TypeNode>, String)>, EncodeError> {
415 let Some(selector) = selector else {
416 return Ok(None);
417 };
418 Ok(selected_case(ast, id, selector)?
419 .map(|(member, member_type)| (member_type, member.name.clone())))
420}
421
422fn discriminant_literal(ast: &Ast, discriminator: Id<TypeNode>, value: i128) -> Expr {
427 let resolved = resolve_alias(ast, discriminator);
428 if matches!(
429 ast.type_(resolved).map(|node| node.kind.clone()),
430 Ok(TypeKind::Enumeration { .. })
431 ) {
432 if let Some(enumerator) = crate::emit::default_enumerator_name(ast, resolved) {
433 return Lua::enumerator_constant(ast, resolved, &enumerator);
434 }
435 }
436 if matches!(
437 resolved_primitive(ast, resolved),
438 Some(PrimitiveKind::Boolean)
439 ) {
440 return Expr::bool(value != 0);
441 }
442 Expr::int(value)
443}
444
445fn resolved_primitive(ast: &Ast, id: Id<TypeNode>) -> Option<PrimitiveKind> {
449 let mut current = id;
450 for _ in 0..MAX_ALIAS_DEPTH {
451 match ast.type_(current).map(|node| node.kind.clone()) {
452 Ok(TypeKind::Primitive(kind)) => return Some(kind),
453 Ok(TypeKind::Alias { aliased }) => current = aliased.resolved()?,
454 _ => return None,
455 }
456 }
457 None
458}
459
460#[allow(clippy::too_many_lines)]
462fn explicit(
463 ast: &Ast,
464 expr: Id<zerodds_ast::ConstExprNode>,
465 type_: Id<TypeNode>,
466 member_name: &str,
467) -> Result<Expr, EncodeError> {
468 let refuse = |kind: &'static str| EncodeError::NoWriter {
469 member: member_name.to_string(),
470 kind,
471 };
472 let node = ast
473 .expr(expr)
474 .map_err(|_| refuse("a `@default` this frontend cannot follow"))?;
475 let resolved = resolve_alias(ast, type_);
476 let kind = ast.type_(resolved).map(|node| node.kind.clone());
477 let is_float128 = matches!(kind, Ok(TypeKind::Primitive(PrimitiveKind::Float128)));
478 match &node.expr {
479 ConstExpr::Literal(AstLiteral::Boolean(flag)) => Ok(Expr::bool(*flag)),
480 ConstExpr::Literal(AstLiteral::Char(ch)) => Ok(Expr::int(i128::from(u32::from(*ch)))),
484 ConstExpr::Literal(AstLiteral::WChar(code)) => Ok(Expr::int(i128::from(*code))),
485 ConstExpr::Literal(AstLiteral::String(value)) => Ok(Expr::string(value)),
486 ConstExpr::Literal(AstLiteral::WString(codes)) => {
487 let mut out = String::new();
488 for code in codes {
489 let Some(ch) = char::from_u32(*code) else {
490 return Err(refuse("a `@default` wide string that is not code points"));
491 };
492 out.push(ch);
493 }
494 Ok(Expr::string(&out))
495 }
496 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
497 let exponent = exponent
498 .as_ref()
499 .map_or(String::new(), |text| format!("e{text}"));
500 let written = format!("{mantissa}{exponent}");
501 if is_float128 {
502 return Ok(Expr::string(&written));
506 }
507 Ok(Expr::Literal(Literal::Float(lua::float_text(&written))))
508 }
509 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => Ok(Expr::string(
510 &crate::adapter::constant::fixed_decimal(digits, *scale),
511 )),
512 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
513 let target = reference
514 .resolved()
515 .ok_or_else(|| refuse("a `@default` enumerator that is unresolved"))?;
516 let enumerator = ast
517 .enumerator(target)
518 .map_err(|_| refuse("a `@default` enumerator this frontend cannot follow"))?;
519 Ok(Lua::enumerator_constant(ast, resolved, &enumerator.name))
520 }
521 _ => {
522 let value = ast
523 .eval_integral(expr)
524 .map_err(|_| refuse("a `@default` this frontend cannot evaluate"))?;
525 Ok(match kind {
526 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => Expr::bool(value != 0),
527 Ok(TypeKind::Primitive(PrimitiveKind::Char | PrimitiveKind::WChar)) => {
531 let point = u32::try_from(value)
532 .ok()
533 .filter(|code| *code <= u32::from(u16::MAX))
534 .ok_or_else(|| {
535 refuse("a `@default` character outside the range of a UTF-16 code unit")
536 })?;
537 Expr::int(i128::from(point))
538 }
539 Ok(TypeKind::Primitive(PrimitiveKind::Float128)) => {
541 Expr::string(&format!("{value}"))
542 }
543 _ => Lua::wide_int(value),
546 })
547 }
548 }
549}
550
551fn calls_in_stat(statement: &Stat, out: &mut Vec<String>) {
560 match statement {
561 Stat::Local { value, .. } => {
562 if let Some(value) = value {
563 calls_in_expr(value, out);
564 }
565 }
566 Stat::Assign { target, value, .. } => {
567 calls_in_place(target, out);
568 calls_in_expr(value, out);
569 }
570 Stat::Function(function) => {
571 for inner in &function.body {
572 calls_in_stat(inner, out);
573 }
574 }
575 Stat::Return(value) => {
576 if let Some(value) = value {
577 calls_in_expr(value, out);
578 }
579 }
580 Stat::Perform(value) => calls_in_expr(value, out),
581 Stat::If {
582 condition,
583 then,
584 otherwise,
585 } => {
586 calls_in_expr(condition, out);
587 for inner in then {
588 calls_in_stat(inner, out);
589 }
590 if let Some(rest) = otherwise {
591 for inner in rest {
592 calls_in_stat(inner, out);
593 }
594 }
595 }
596 Stat::While { condition, body } => {
597 calls_in_expr(condition, out);
598 for inner in body {
599 calls_in_stat(inner, out);
600 }
601 }
602 Stat::NumericFor { from, to, body, .. } => {
603 calls_in_expr(from, out);
604 calls_in_expr(to, out);
605 for inner in body {
606 calls_in_stat(inner, out);
607 }
608 }
609 Stat::Break | Stat::Comment(_) | Stat::Annotation(_) | Stat::Verbatim(_) => {}
610 }
611}
612
613fn calls_in_expr(expr: &Expr, out: &mut Vec<String>) {
618 match expr {
619 Expr::Call { func, args } => {
620 if let Place::Field { base, name } = func.as_ref() {
623 if matches!(base.as_ref(), Place::Name(root) if root.as_str() == CODECS)
624 && name.as_str().starts_with("zd_def_")
625 && !out.iter().any(|seen| seen == name.as_str())
626 {
627 out.push(name.as_str().to_string());
628 }
629 }
630 calls_in_place(func, out);
631 for arg in args {
632 calls_in_expr(arg, out);
633 }
634 }
635 Expr::Table(table) => {
636 for entry in &table.entries {
637 if let lua::Key::Expr(key) = &entry.key {
638 calls_in_expr(key, out);
639 }
640 calls_in_expr(&entry.value, out);
641 }
642 }
643 Expr::Place(place) => calls_in_place(place, out),
644 Expr::Binary { lhs, rhs, .. } => {
645 calls_in_expr(lhs, out);
646 calls_in_expr(rhs, out);
647 }
648 Expr::Unary { value, .. } | Expr::Paren(value) => calls_in_expr(value, out),
649 Expr::Nil | Expr::Literal(_) => {}
650 }
651}
652
653fn calls_in_place(place: &Place, out: &mut Vec<String>) {
657 match place {
658 Place::Name(_) => {}
659 Place::Field { base, .. } => calls_in_place(base, out),
660 Place::Index { base, key } => {
661 calls_in_place(base, out);
662 calls_in_expr(key, out);
663 }
664 Place::Paren(inner) => calls_in_expr(inner, out),
665 }
666}
667
668fn codec_call(callee: &str, args: Vec<Expr>) -> Expr {
671 Expr::Call {
672 func: Box::new(Place::name(CODECS).field(callee)),
673 args,
674 }
675}
676
677fn place_of(value: Expr) -> Place {
678 match value {
679 Expr::Place(place) => place,
680 Expr::Paren(inner) => match *inner {
681 Expr::Place(place) => place,
682 other => Place::Paren(Box::new(other)),
683 },
684 other => Place::Paren(Box::new(other)),
685 }
686}