1use crate::adapter::fsharp::{field_name, fsharp_type_name};
44use crate::default_common::{discriminator_default_value, resolve_alias};
45use crate::emit::default_enumerator_name;
46use crate::encode::EncodeError;
47use crate::fixed_literal::{fixed_text, 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, Ident, Literal};
53use zerodds_lang_cst::fsharp;
54
55const MAX_ALIAS_DEPTH: usize = 64;
58
59pub fn default_expr(ast: &Ast, id: Id<TypeNode>) -> Result<fsharp::Expr, EncodeError> {
68 default_expr_at(ast, id, 0)
69}
70
71fn default_expr_at(ast: &Ast, id: Id<TypeNode>, depth: usize) -> Result<fsharp::Expr, EncodeError> {
75 if depth > MAX_ALIAS_DEPTH {
76 return Err(EncodeError::NoWriter {
77 member: fsharp_type_name(ast, id),
78 kind: "type nested past the recursion bound",
79 });
80 }
81 let id = resolve_alias(ast, id);
82 let name = fsharp_type_name(ast, id);
83 let refuse = |kind: &'static str| EncodeError::NoWriter {
84 member: name.clone(),
85 kind,
86 };
87 let Ok(node) = ast.type_(id) else {
88 return Err(refuse("type that does not resolve"));
89 };
90 Ok(match &node.kind {
91 TypeKind::Primitive(kind) => primitive_default(*kind),
92
93 TypeKind::String { .. } => text(""),
96
97 TypeKind::Fixed { scale, .. } => text(&zero_decimal(*scale)),
99
100 TypeKind::Alias { .. } => return Err(refuse("typedef with an unresolved target")),
101
102 TypeKind::Enumeration { .. } => {
105 let enumerator = default_enumerator_name(ast, id)
106 .ok_or_else(|| refuse("enumeration with no enumerator to take as its default"))?;
107 enumerator_expr(ast, id, &enumerator)
108 }
109
110 TypeKind::Bitmask { .. } => {
115 let bound = ast.effective_bit_bound(id);
116 crate::adapter::fsharp::typed_int(
117 crate::adapter::fsharp::fsharp_holder(bound),
118 fsharp::Expr::int(0),
119 )
120 }
121
122 TypeKind::Bitset { .. } => fsharp::Expr::new(&name, Vec::new()),
126
127 TypeKind::Sequence { .. } | TypeKind::Map { .. } => fsharp::Expr::ArrayLit(Vec::new()),
130
131 TypeKind::Array { element, dims } => {
135 let element = element
136 .resolved()
137 .ok_or_else(|| refuse("array with an unresolved element"))?;
138 let mut lengths = Vec::new();
139 for dim in dims {
140 let length = ast
141 .eval_integral(*dim)
142 .map_err(|_| refuse("array whose length does not evaluate"))?;
143 let length =
144 usize::try_from(length).map_err(|_| refuse("array of a negative length"))?;
145 lengths.push(length);
146 }
147 let inner = default_expr_at(ast, element, depth + 1)?;
148 let mut out = inner;
149 for length in lengths.into_iter().rev() {
150 out = fsharp::Expr::ArrayLit(core::iter::repeat_n(out.clone(), length).collect());
151 }
152 out
153 }
154
155 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
161 fsharp::Expr::new(&name, Vec::new())
162 }
163
164 TypeKind::Union { .. } => fsharp::Expr::new(&name, Vec::new()),
173
174 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
175 return Err(refuse("construct with no F# default value"));
176 }
177 TypeKind::ValueBox { .. } => {
178 return Err(refuse(
179 "boxed value, whose presence flag has no settled wire form",
180 ));
181 }
182 TypeKind::Any => {
183 return Err(refuse(
184 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
185 as a TypeCode ahead of the value",
186 ));
187 }
188 TypeKind::ValueBase => {
189 return Err(refuse(
190 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
191 one, as a chunked value with its repository id",
192 ));
193 }
194 })
195}
196
197pub fn discriminator_default(
206 ast: &Ast,
207 discriminator: Id<TypeNode>,
208) -> Result<fsharp::Expr, EncodeError> {
209 let resolved = resolve_alias(ast, discriminator);
210 let is_enum = matches!(
211 ast.type_(resolved).map(|node| node.kind.clone()),
212 Ok(TypeKind::Enumeration { .. })
213 );
214 if is_enum {
215 if let Some(enumerator) = default_enumerator_name(ast, resolved) {
216 return Ok(enumerator_expr(ast, resolved, &enumerator));
217 }
218 }
219 match discriminator_default_value(ast, resolved) {
220 Some(value) if resolved_primitive(ast, resolved) == Some(PrimitiveKind::Boolean) => {
221 Ok(fsharp::Expr::Literal(Literal::Bool(value != 0)))
222 }
223 Some(value) => Ok(match resolved_primitive(ast, resolved) {
224 Some(kind) => match crate::adapter::fsharp::fsharp_primitive_type(kind) {
225 fsharp::Type::Primitive(primitive) => {
226 crate::adapter::fsharp::typed_int(primitive, fsharp::Expr::int(value))
227 }
228 _ => fsharp::Expr::int(value),
229 },
230 None => fsharp::Expr::int(value),
231 }),
232 None => default_expr(ast, resolved),
233 }
234}
235
236pub fn member_value(
245 ast: &Ast,
246 node: &MemberNode,
247 type_: Id<TypeNode>,
248) -> Result<fsharp::Expr, EncodeError> {
249 match node.facets.xtypes.as_ref().and_then(|x| x.default_value) {
250 Some(expr) => explicit(ast, expr, type_, &node.name),
251 None => Ok(fsharp::Expr::call(&defaulter_name(ast, type_), Vec::new())),
257 }
258}
259
260#[must_use]
265pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
266 match ast.type_(id).map(|node| node.kind.clone()) {
267 Ok(TypeKind::Primitive(kind)) => {
268 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
269 }
270 _ => format!("zd_def_{}", crate::emit::fsharp::type_tag(ast, id, 0)),
271 }
272}
273
274#[must_use]
276pub fn defaulter_from_decoder(decoder: &str) -> String {
277 match decoder.strip_prefix("zd_dec_") {
278 Some(rest) => format!("zd_def_{rest}"),
279 None => format!("zd_def_{decoder}"),
280 }
281}
282
283pub fn fsharp_defaulters_over(
291 ast: &Ast,
292 types: &[Id<TypeNode>],
293) -> Result<Vec<fsharp::Decl>, EncodeError> {
294 let mut out = Vec::new();
295 let mut emitted: Vec<String> = Vec::new();
296 for id in types.iter().copied() {
297 let name = defaulter_name(ast, id);
298 if emitted.contains(&name) {
299 continue;
300 }
301 let returns = fsharp_value_type_or(ast, id, &name)?;
302 let value = default_expr(ast, id)?;
303 emitted.push(name.clone());
304 out.push(fsharp::Decl::Let(fsharp::LetDecl {
305 doc: Doc::line(
306 "Table 9 of DDS-XTypes 1.3: what this type is where the stream did not \
307 carry it. `unit -> T` so every call site gets a value of its own.",
308 ),
309 name: Ident::new(&name),
310 params: vec![fsharp::FnParam {
311 name: Ident::new("_zd_unit"),
312 type_: fsharp::Type::Primitive(fsharp::Primitive::Unit),
313 }],
314 type_: Some(returns),
315 body: vec![fsharp::Stmt::Return(value)],
316 }));
317 }
318 Ok(out)
319}
320
321fn fsharp_value_type_or(
325 ast: &Ast,
326 id: Id<TypeNode>,
327 at: &str,
328) -> Result<fsharp::Type, EncodeError> {
329 crate::adapter::fsharp::fsharp_value_type(ast, resolve_alias(ast, id), at).map_err(|_| {
330 EncodeError::NoWriter {
331 member: at.to_string(),
332 kind: "type with no F# form to return",
333 }
334 })
335}
336
337pub fn fsharp_defaulters_called_by(
345 ast: &Ast,
346 decls: &[fsharp::Decl],
347) -> Result<Vec<fsharp::Decl>, EncodeError> {
348 let index: Vec<(String, Id<TypeNode>)> = ast
349 .types()
350 .map(|(id, _)| (defaulter_name(ast, id), id))
351 .collect();
352 let mut called: Vec<String> = Vec::new();
353 for decl in decls {
354 calls_in_decl(decl, &mut called);
355 }
356 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
357 let mut have: Vec<String> = Vec::new();
358 for _ in 0..=index.len() {
359 let mut grew = false;
360 for name in &called {
361 if have.contains(name) {
362 continue;
363 }
364 have.push(name.clone());
365 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
366 wanted.push(id);
367 grew = true;
368 }
369 }
370 let produced = fsharp_defaulters_over(ast, &wanted)?;
371 if !grew {
372 return Ok(produced);
373 }
374 for decl in &produced {
375 calls_in_decl(decl, &mut called);
376 }
377 }
378 fsharp_defaulters_over(ast, &wanted)
379}
380
381fn calls_in_decl(decl: &fsharp::Decl, out: &mut Vec<String>) {
382 if let fsharp::Decl::Let(let_decl) = decl {
383 calls_in_body(&let_decl.body, out);
384 }
385}
386
387fn calls_in_body(body: &[fsharp::Stmt], out: &mut Vec<String>) {
392 for statement in body {
393 calls_in_stmt(statement, out);
394 }
395}
396
397fn calls_in_stmt(statement: &fsharp::Stmt, out: &mut Vec<String>) {
402 match statement {
403 fsharp::Stmt::Let { value, .. } => calls_in_expr(value, out),
404 fsharp::Stmt::Assign { place, value } => {
405 calls_in_expr(place, out);
406 calls_in_expr(value, out);
407 }
408 fsharp::Stmt::Expr(value) | fsharp::Stmt::Return(value) => calls_in_expr(value, out),
409 fsharp::Stmt::If {
410 condition,
411 then,
412 otherwise,
413 } => {
414 calls_in_expr(condition, out);
415 calls_in_body(then, out);
416 if let Some(otherwise) = otherwise {
417 calls_in_body(otherwise, out);
418 }
419 }
420 fsharp::Stmt::CountUp { bound, body, .. } => {
421 calls_in_expr(bound, out);
422 calls_in_body(body, out);
423 }
424 fsharp::Stmt::Until { condition, body } => {
425 calls_in_expr(condition, out);
426 calls_in_body(body, out);
427 }
428 fsharp::Stmt::Guarded { body } => calls_in_body(body, out),
429 fsharp::Stmt::Leave | fsharp::Stmt::GiveBack | fsharp::Stmt::Comment(_) => {}
430 }
431}
432
433fn calls_in_expr(expr: &fsharp::Expr, out: &mut Vec<String>) {
438 match expr {
439 fsharp::Expr::Call { func, args } | fsharp::Expr::CurriedCall { func, args } => {
440 if let fsharp::Expr::Name(name) = func.as_ref() {
441 let name = name.as_str();
442 if name.starts_with("zd_def_") && !out.iter().any(|seen| seen == name) {
443 out.push(name.to_string());
444 }
445 }
446 calls_in_expr(func, out);
447 for arg in args {
448 calls_in_expr(arg, out);
449 }
450 }
451 fsharp::Expr::Binary { lhs, rhs, .. } => {
452 calls_in_expr(lhs, out);
453 calls_in_expr(rhs, out);
454 }
455 fsharp::Expr::Paren(value) | fsharp::Expr::Unary { value, .. } => {
456 calls_in_expr(value, out);
457 }
458 fsharp::Expr::Dot { value, .. } => calls_in_expr(value, out),
459 fsharp::Expr::Index { value, index } => {
460 calls_in_expr(value, out);
461 calls_in_expr(index, out);
462 }
463 fsharp::Expr::Some_(value) => calls_in_expr(value, out),
464 fsharp::Expr::New { args, .. } => {
465 for arg in args {
466 calls_in_expr(arg, out);
467 }
468 }
469 fsharp::Expr::ObjectInit { fields, .. } => {
470 for (_, value) in fields {
471 calls_in_expr(value, out);
472 }
473 }
474 fsharp::Expr::ArrayLit(elements) => {
475 for element in elements {
476 calls_in_expr(element, out);
477 }
478 }
479 fsharp::Expr::ArrayZeroCreate { count, .. } => calls_in_expr(count, out),
480 fsharp::Expr::Literal(_) | fsharp::Expr::Name(_) | fsharp::Expr::None_ => {}
481 }
482}
483
484fn primitive_default(kind: PrimitiveKind) -> fsharp::Expr {
489 match kind {
490 PrimitiveKind::Boolean => fsharp::Expr::Literal(Literal::Bool(false)),
491 PrimitiveKind::Float64 => fsharp::Expr::Literal(Literal::Float("0.0".to_string())),
498 PrimitiveKind::Float32 => fsharp::Expr::call(
499 "float32",
500 vec![fsharp::Expr::Literal(Literal::Float("0.0".to_string()))],
501 ),
502 PrimitiveKind::Float128 => text("0"),
503 other => match crate::adapter::fsharp::fsharp_primitive_type(other) {
509 fsharp::Type::Primitive(primitive) => {
510 crate::adapter::fsharp::typed_int(primitive, fsharp::Expr::int(0))
511 }
512 _ => fsharp::Expr::int(0),
513 },
514 }
515}
516
517fn enumerator_expr(ast: &Ast, id: Id<TypeNode>, enumerator: &str) -> fsharp::Expr {
519 fsharp::Expr::dot(
520 fsharp::Expr::Name(Ident::new(&fsharp_type_name(ast, id))),
521 &field_name(enumerator),
522 )
523}
524
525fn resolved_primitive(ast: &Ast, id: Id<TypeNode>) -> Option<PrimitiveKind> {
526 let mut current = id;
527 for _ in 0..MAX_ALIAS_DEPTH {
528 match ast.type_(current).map(|node| node.kind.clone()) {
529 Ok(TypeKind::Primitive(kind)) => return Some(kind),
530 Ok(TypeKind::Alias { aliased }) => current = aliased.resolved()?,
531 _ => return None,
532 }
533 }
534 None
535}
536
537#[allow(clippy::too_many_lines)]
539fn explicit(
540 ast: &Ast,
541 expr: Id<zerodds_ast::ConstExprNode>,
542 type_: Id<TypeNode>,
543 member_name: &str,
544) -> Result<fsharp::Expr, EncodeError> {
545 let refuse = |kind: &'static str| EncodeError::NoWriter {
546 member: member_name.to_string(),
547 kind,
548 };
549 let node = ast
550 .expr(expr)
551 .map_err(|_| refuse("a `@default` this frontend cannot follow"))?;
552 let resolved = resolve_alias(ast, type_);
553 let kind = ast.type_(resolved).map(|node| node.kind.clone());
554 match &node.expr {
555 ConstExpr::Literal(AstLiteral::Boolean(flag)) => {
556 Ok(fsharp::Expr::Literal(Literal::Bool(*flag)))
557 }
558 ConstExpr::Literal(AstLiteral::Char(ch)) => Ok(crate::adapter::fsharp::typed_int(
559 fsharp::Primitive::Byte,
560 fsharp::Expr::int(i128::from(u32::from(*ch))),
561 )),
562 ConstExpr::Literal(AstLiteral::WChar(code)) => Ok(crate::adapter::fsharp::typed_int(
563 fsharp::Primitive::UInt16,
564 fsharp::Expr::int(i128::from(*code)),
565 )),
566 ConstExpr::Literal(AstLiteral::String(value)) => Ok(text(value)),
567 ConstExpr::Literal(AstLiteral::WString(codes)) => {
568 let mut out = String::new();
569 for code in codes {
570 let ch = char::from_u32(*code)
571 .ok_or_else(|| refuse("a `@default` wide string that is not code points"))?;
572 out.push(ch);
573 }
574 Ok(text(&out))
575 }
576 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
577 let exponent = exponent
578 .as_ref()
579 .map_or_else(String::new, |exponent| format!("e{exponent}"));
580 let literal = fsharp::Expr::Literal(Literal::Float(format!("{mantissa}{exponent}")));
581 if matches!(kind, Ok(TypeKind::Primitive(PrimitiveKind::Float32))) {
586 Ok(fsharp::Expr::call("float32", vec![literal]))
587 } else {
588 Ok(literal)
589 }
590 }
591 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => {
592 Ok(text(&fixed_text(digits, *scale)))
593 }
594 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
595 let target = reference
596 .resolved()
597 .ok_or_else(|| refuse("a `@default` enumerator that is unresolved"))?;
598 let enumerator = ast
599 .enumerator(target)
600 .map_err(|_| refuse("a `@default` enumerator this frontend cannot follow"))?;
601 Ok(enumerator_expr(ast, resolved, &enumerator.name))
602 }
603 _ => {
604 let value = ast
605 .eval_integral(expr)
606 .map_err(|_| refuse("a `@default` this frontend cannot evaluate"))?;
607 Ok(match kind {
608 Ok(TypeKind::Primitive(PrimitiveKind::Boolean)) => {
609 fsharp::Expr::Literal(Literal::Bool(value != 0))
610 }
611 Ok(TypeKind::Primitive(PrimitiveKind::Char)) => {
612 if !(0..=0xFF).contains(&value) {
613 return Err(refuse("a `@default` character outside one octet"));
614 }
615 crate::adapter::fsharp::typed_int(
616 fsharp::Primitive::Byte,
617 fsharp::Expr::int(value),
618 )
619 }
620 Ok(TypeKind::Primitive(PrimitiveKind::WChar)) => {
621 if !(0..=0xFFFF).contains(&value) {
622 return Err(refuse(
623 "a `@default` character outside the range of a UTF-16 code unit",
624 ));
625 }
626 crate::adapter::fsharp::typed_int(
627 fsharp::Primitive::UInt16,
628 fsharp::Expr::int(value),
629 )
630 }
631 Ok(TypeKind::Primitive(primitive)) => {
641 match crate::adapter::fsharp::fsharp_primitive_type(primitive) {
642 fsharp::Type::Primitive(target) => {
643 crate::adapter::fsharp::typed_int(target, fsharp::Expr::int(value))
644 }
645 _ => fsharp::Expr::int(value),
646 }
647 }
648 _ => fsharp::Expr::int(value),
649 })
650 }
651 }
652}
653
654fn text(value: &str) -> fsharp::Expr {
655 fsharp::Expr::Literal(Literal::Str(value.to_string()))
656}