1use crate::adapter::csharp::{csharp_member_type, declared_path, field_name, type_tag};
53use crate::adapter::cst::MaterialiseError;
54use crate::default_common::{discriminator_default_value, resolve_alias, selected_case};
55use crate::emit::csharp::CSharp;
56use crate::encode::EncodeError;
57use crate::fixed_literal::{fixed_text, zero_decimal};
58use zerodds_ast::{
59 Ast, ConstExpr, Literal as AstLiteral, MemberNode, PrimitiveKind, TypeKind, TypeNode,
60};
61use zerodds_ast_core::Id;
62use zerodds_lang_cst::common::{Doc, Ident, Literal, Path};
63use zerodds_lang_cst::csharp;
64
65#[must_use]
72pub fn defaulter_name(ast: &Ast, id: Id<TypeNode>) -> String {
73 match ast.type_(id).map(|node| node.kind.clone()) {
74 Ok(TypeKind::Primitive(kind)) => {
75 format!("zd_def_p_{}", kind.as_str().replace(' ', "_"))
76 }
77 _ => format!(
78 "zd_def_{}",
79 type_tag(ast, id, 0).unwrap_or_else(|_| format!("unresolved_type_{}", id.slot()))
80 ),
81 }
82}
83
84#[must_use]
93pub fn defaulter_from_decoder(decoder: &str) -> String {
94 match decoder.strip_prefix("zd_dec_") {
95 Some(rest) => format!("zd_def_{rest}"),
96 None => format!("zd_def_{decoder}"),
97 }
98}
99
100pub fn default_into(ast: &Ast, id: Id<TypeNode>, place: csharp::Expr) -> csharp::Statement {
102 assign(place, call(&defaulter_name(ast, id), Vec::new()))
103}
104
105pub fn member_default(
118 ast: &Ast,
119 node: &MemberNode,
120 type_: Id<TypeNode>,
121 place: csharp::Expr,
122) -> Result<Vec<csharp::Statement>, EncodeError> {
123 let Some(expr) = node.facets.xtypes.as_ref().and_then(|x| x.default_value) else {
124 return Ok(vec![default_into(ast, type_, place)]);
125 };
126 let value = explicit(ast, expr, type_, &node.name)?;
127 Ok(vec![assign(place, value)])
128}
129
130pub fn csharp_defaulters_called_by(
142 ast: &Ast,
143 members: &[csharp::Member],
144) -> Result<Vec<csharp::Member>, EncodeError> {
145 let index: Vec<(String, Id<TypeNode>)> = ast
146 .types()
147 .map(|(id, _)| (defaulter_name(ast, id), id))
148 .collect();
149 let mut called: Vec<String> = Vec::new();
150 for member in members {
151 calls_in_member(member, &mut called);
152 }
153 let mut wanted: Vec<Id<TypeNode>> = Vec::new();
154 let mut have: Vec<String> = Vec::new();
155 for _ in 0..=index.len() {
159 let mut grew = false;
160 for name in &called {
161 if have.contains(name) {
162 continue;
163 }
164 have.push(name.clone());
165 if let Some(id) = index.iter().find(|(n, _)| n == name).map(|(_, id)| *id) {
166 wanted.push(id);
167 grew = true;
168 }
169 }
170 let produced = csharp_defaulters_over(ast, &wanted)?;
171 if !grew {
172 return Ok(produced);
173 }
174 for member in &produced {
175 calls_in_member(member, &mut called);
176 }
177 }
178 csharp_defaulters_over(ast, &wanted)
179}
180
181pub fn csharp_defaulters_over(
186 ast: &Ast,
187 types: &[Id<TypeNode>],
188) -> Result<Vec<csharp::Member>, EncodeError> {
189 let mut out = Vec::new();
190 let mut emitted: Vec<String> = Vec::new();
191 for id in types.iter().copied() {
192 let name = defaulter_name(ast, id);
193 if emitted.contains(&name) {
194 continue;
195 }
196 let Some(body) = body_of(ast, id, &name)? else {
197 continue;
198 };
199 emitted.push(name.clone());
200 out.push(csharp::Member::Method(csharp::Method {
201 doc: Doc::line(
202 "Table 9 of DDS-XTypes 1.3: what this type is where the stream did not \
203 carry it.",
204 ),
205 attributes: Vec::new(),
206 modifiers: vec![csharp::Modifier::Private, csharp::Modifier::Static],
207 returns: value_type(ast, id),
208 name: Ident::new(&name),
209 generics: Vec::new(),
210 params: Vec::new(),
211 constraints: Vec::new(),
212 body: Some(csharp::Block { statements: body }),
213 expression: None,
214 }));
215 }
216 Ok(out)
217}
218
219fn value_type(ast: &Ast, id: Id<TypeNode>) -> csharp::Type {
221 csharp_member_type(ast, id, "default").unwrap_or_else(|_| {
222 csharp::Type::named(Path::new(
223 &[format!("UnresolvedType{}", id.slot()).as_str()],
224 ))
225 })
226}
227
228#[allow(clippy::too_many_lines)]
231fn body_of(
232 ast: &Ast,
233 id: Id<TypeNode>,
234 own_name: &str,
235) -> Result<Option<Vec<csharp::Statement>>, EncodeError> {
236 let refuse = |kind: &'static str| EncodeError::NoWriter {
237 member: own_name.to_string(),
238 kind,
239 };
240 let Ok(node) = ast.type_(id) else {
248 return Err(refuse("a type the model does not hold"));
249 };
250 let statements = match &node.kind {
251 TypeKind::Primitive(kind) => vec![give_back(primitive_default(*kind))],
252
253 TypeKind::String { .. } => vec![give_back(text(""))],
256
257 TypeKind::Fixed { scale, .. } => vec![give_back(text(&zero_decimal(*scale)))],
260
261 TypeKind::Alias { aliased } => {
262 let Some(target) = aliased.resolved() else {
263 return Err(refuse("typedef with an unresolved target"));
264 };
265 vec![give_back(call(&defaulter_name(ast, target), Vec::new()))]
266 }
267
268 TypeKind::Enumeration { .. } => {
271 let Some(enumerator) = crate::emit::default_enumerator_name(ast, id) else {
272 return Err(refuse(
273 "enumeration with no enumerator to take as its default",
274 ));
275 };
276 vec![give_back(enumerator_constant(ast, id, &enumerator))]
277 }
278
279 TypeKind::Bitmask { .. } => vec![give_back(csharp::Expr::Cast {
285 type_: Box::new(value_type(ast, id)),
286 value: Box::new(csharp::Expr::Literal(Literal::int(0))),
287 })],
288
289 TypeKind::Bitset { .. } => {
290 let mut statements = vec![new_local(value_type(ast, id))];
291 for field in &ast.all_bitfields_of(id) {
292 let Ok(field_node) = ast.bitfield(*field) else {
293 continue;
294 };
295 if let Some(name) = field_node.name.as_ref() {
297 statements.push(assign(
298 member("v", &field_name(name)),
299 csharp::Expr::Literal(Literal::int(0)),
300 ));
301 }
302 }
303 statements.push(give_back(name("v")));
304 statements
305 }
306
307 TypeKind::Sequence { .. } | TypeKind::Map { .. } => {
310 let csharp::Type::Array { .. } = value_type(ast, id) else {
311 return Err(refuse("collection whose C# form is not an array"));
312 };
313 let (element, nested) = CSharp::peel(value_type(ast, id));
314 vec![give_back(csharp::Expr::NewArray {
315 element: Box::new(element),
316 length: Some(Box::new(csharp::Expr::Literal(Literal::int(0)))),
317 elements: Vec::new(),
318 nested: nested.saturating_sub(1),
319 })]
320 }
321
322 TypeKind::Array { element, dims } => {
329 let Some(element) = element.resolved() else {
330 return Err(refuse("array with an unresolved element"));
331 };
332 let mut lengths = Vec::new();
333 for dim in dims {
334 lengths.push(
335 ast.eval_integral(*dim)
336 .map_err(|_| refuse("array whose length does not evaluate"))?,
337 );
338 }
339 let (innermost, total) = CSharp::peel(value_type(ast, id));
349 let ranks = lengths.len();
350 let mut statements = vec![csharp::Statement::Declare {
351 type_: value_type(ast, id),
352 name: Ident::new("v"),
353 value: Some(csharp::Expr::NewArray {
354 element: Box::new(innermost.clone()),
355 length: Some(Box::new(csharp::Expr::Literal(Literal::int(lengths[0])))),
356 elements: Vec::new(),
357 nested: total - 1,
358 }),
359 }];
360
361 let place = |depth: usize| {
365 let mut access = name("v");
366 for level in 0..=depth {
367 access = csharp::Expr::Index {
368 value: Box::new(access),
369 index: Box::new(name(&format!("zd_i{level}"))),
370 };
371 }
372 access
373 };
374 let mut inner = vec![default_into(ast, element, place(ranks - 1))];
375 for level in (0..ranks).rev() {
376 let mut body = Vec::new();
377 if level + 1 < ranks {
378 body.push(assign(
379 place(level),
380 csharp::Expr::NewArray {
381 element: Box::new(innermost.clone()),
382 length: Some(Box::new(csharp::Expr::Literal(Literal::int(
383 lengths[level + 1],
384 )))),
385 elements: Vec::new(),
386 nested: total - level - 2,
387 },
388 ));
389 }
390 body.extend(inner);
391 inner = vec![counted_loop(
392 &format!("zd_i{level}"),
393 csharp::Expr::Literal(Literal::int(lengths[level])),
394 csharp::Block { statements: body },
395 )];
396 }
397 statements.extend(inner);
398 statements.push(give_back(name("v")));
399 statements
400 }
401
402 TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
405 let mut statements = vec![new_local(value_type(ast, id))];
406 for member_id in &ast.all_members_of(id) {
407 let Ok(member_node) = ast.member(*member_id) else {
408 continue;
409 };
410 let field = field_name(&member_node.name);
411 if member_node.facets.is_optional() {
412 statements.push(assign(
418 member("v", &field),
419 csharp::Expr::Literal(Literal::Null),
420 ));
421 continue;
422 }
423 let Some(member_type) = member_node.type_.resolved() else {
424 return Err(EncodeError::NoWriter {
425 member: member_node.name.clone(),
426 kind: "member with an unresolved type",
427 });
428 };
429 statements.extend(member_default(
430 ast,
431 member_node,
432 member_type,
433 member("v", &field),
434 )?);
435 }
436 statements.push(give_back(name("v")));
437 statements
438 }
439
440 TypeKind::Union { discriminator, .. } => {
445 let Some(discriminator) = discriminator.resolved() else {
446 return Err(refuse("union with an unresolved discriminator"));
447 };
448 let mut statements = vec![
449 new_local(value_type(ast, id)),
450 default_into(ast, discriminator, member("v", &field_name("_d"))),
451 ];
452 if let Some((member_type, field)) = selected_by_default(ast, id, discriminator)? {
453 statements.push(default_into(ast, member_type, member("v", &field)));
454 }
455 statements.push(give_back(name("v")));
456 statements
457 }
458
459 TypeKind::Native | TypeKind::ObjectRef { .. } | TypeKind::AnnotationType { .. } => {
460 return Ok(None);
461 }
462 TypeKind::ValueBox { .. } => {
463 return Err(refuse(
464 "boxed value, whose presence flag has no settled wire form",
465 ));
466 }
467 TypeKind::Any => {
473 return Err(refuse(
474 "`any`, which DDS-XTypes has no encoding for; only CORBA CDR writes one, \
475 as a TypeCode ahead of the value",
476 ));
477 }
478 TypeKind::ValueBase => {
479 return Err(refuse(
480 "`ValueBase`, which DDS-XTypes has no encoding for; only CORBA CDR writes \
481 one, as a chunked value with its repository id",
482 ));
483 }
484 };
485 Ok(Some(statements))
486}
487
488fn primitive_default(kind: PrimitiveKind) -> csharp::Expr {
495 match kind {
496 PrimitiveKind::Boolean => csharp::Expr::Literal(Literal::Bool(false)),
497 PrimitiveKind::Char | PrimitiveKind::WChar => csharp::Expr::Literal(Literal::Char('\0')),
500 PrimitiveKind::Float32 => csharp::Expr::Literal(Literal::Float("0.0f".to_string())),
503 PrimitiveKind::Float64 => csharp::Expr::Literal(Literal::Float("0.0".to_string())),
504 PrimitiveKind::Float128 => csharp::Expr::Member {
507 value: Box::new(csharp::Expr::Path(Path::rooted(&[
508 "ZeroDds", "Idl", "Float128",
509 ]))),
510 name: Ident::new("Zero"),
511 },
512 PrimitiveKind::Int8
517 | PrimitiveKind::Int16
518 | PrimitiveKind::Int32
519 | PrimitiveKind::Int64
520 | PrimitiveKind::Octet
521 | PrimitiveKind::UInt8
522 | PrimitiveKind::UInt16
523 | PrimitiveKind::UInt32
524 | PrimitiveKind::UInt64 => csharp::Expr::Literal(Literal::int(0)),
525 }
526}
527
528pub(crate) fn enumerator_constant(ast: &Ast, id: Id<TypeNode>, enumerator: &str) -> csharp::Expr {
531 let owner = declared_path(ast, id).unwrap_or_else(|_| {
532 format!("UnresolvedEnum{}", id.slot())
536 });
537 let name = zerodds_lang_cst::csharp::escape_keyword(enumerator);
541 csharp::Expr::Member {
542 value: Box::new(path(&owner)),
543 name: Ident::new(&name),
544 }
545}
546
547fn selected_by_default(
549 ast: &Ast,
550 id: Id<TypeNode>,
551 discriminator: Id<TypeNode>,
552) -> Result<Option<(Id<TypeNode>, String)>, EncodeError> {
553 let Some(selector) = discriminator_default_value(ast, discriminator) else {
554 return Ok(None);
555 };
556 Ok(selected_case(ast, id, selector)?
557 .map(|(member, member_type)| (member_type, field_name(&member.name))))
558}
559
560#[allow(clippy::too_many_lines)]
562fn explicit(
563 ast: &Ast,
564 expr: Id<zerodds_ast::ConstExprNode>,
565 type_: Id<TypeNode>,
566 member_name: &str,
567) -> Result<csharp::Expr, EncodeError> {
568 let refuse = |kind: &'static str| EncodeError::NoWriter {
569 member: member_name.to_string(),
570 kind,
571 };
572 let node = ast
573 .expr(expr)
574 .map_err(|_| refuse("a `@default` this frontend cannot follow"))?;
575 let resolved = resolve_alias(ast, type_);
576 let kind = ast.type_(resolved).map(|node| node.kind.clone());
577 match &node.expr {
578 ConstExpr::Literal(AstLiteral::Boolean(flag)) => {
579 Ok(csharp::Expr::Literal(Literal::Bool(*flag)))
580 }
581 ConstExpr::Literal(AstLiteral::Char(ch)) => char_literal(u32::from(*ch), refuse),
582 ConstExpr::Literal(AstLiteral::WChar(code)) => char_literal(*code, refuse),
583 ConstExpr::Literal(AstLiteral::String(value)) => Ok(text(value)),
584 ConstExpr::Literal(AstLiteral::WString(codes)) => {
585 let mut out = String::new();
586 for code in codes {
587 let Some(ch) = char::from_u32(*code) else {
588 return Err(refuse("a `@default` wide string that is not code points"));
589 };
590 out.push(ch);
591 }
592 Ok(text(&out))
593 }
594 ConstExpr::Literal(AstLiteral::Float { mantissa, exponent }) => {
595 let suffix = match kind {
596 Ok(TypeKind::Primitive(PrimitiveKind::Float32)) => "f",
597 _ => "",
598 };
599 let exponent = exponent
600 .as_ref()
601 .map_or(String::new(), |text| format!("e{text}"));
602 Ok(csharp::Expr::Literal(Literal::Float(format!(
603 "{mantissa}{exponent}{suffix}"
604 ))))
605 }
606 ConstExpr::Literal(AstLiteral::Fixed { digits, scale }) => {
609 Ok(text(&fixed_text(digits, *scale)))
610 }
611 ConstExpr::Literal(AstLiteral::Enumerator(reference)) => {
612 let target = reference
613 .resolved()
614 .ok_or_else(|| refuse("a `@default` enumerator that is unresolved"))?;
615 let enumerator = ast
616 .enumerator(target)
617 .map_err(|_| refuse("a `@default` enumerator this frontend cannot follow"))?;
618 Ok(enumerator_constant(ast, resolved, &enumerator.name))
619 }
620 _ => {
621 let value = ast
624 .eval_integral(expr)
625 .map_err(|_| refuse("a `@default` this frontend cannot evaluate"))?;
626 Ok(match kind {
627 Ok(TypeKind::Primitive(PrimitiveKind::Char | PrimitiveKind::WChar)) => {
628 let code = u32::try_from(value).map_err(|_| {
633 refuse("a `@default` character outside the range of a code unit")
634 })?;
635 char_literal(code, refuse)?
636 }
637 Ok(TypeKind::Enumeration { .. } | TypeKind::Bitmask { .. }) => csharp::Expr::Cast {
641 type_: Box::new(value_type(ast, resolved)),
642 value: Box::new(csharp::Expr::Literal(Literal::int(value))),
643 },
644 _ => csharp::Expr::Literal(Literal::int(value)),
645 })
646 }
647 }
648}
649
650fn char_literal(
666 code: u32,
667 refuse: impl Fn(&'static str) -> EncodeError,
668) -> Result<csharp::Expr, EncodeError> {
669 if code > u32::from(u16::MAX) {
670 return Err(refuse(
671 "a `@default` character above the Basic Multilingual Plane, \
672 which no single C# `char` holds",
673 ));
674 }
675 Ok(csharp::Expr::Cast {
676 type_: Box::new(csharp::Type::Predefined(csharp::Predefined::Char)),
677 value: Box::new(csharp::Expr::Literal(Literal::int(i128::from(code)))),
678 })
679}
680
681fn calls_in_member(member: &csharp::Member, out: &mut Vec<String>) {
684 match member {
685 csharp::Member::Method(method) => {
686 if let Some(body) = &method.body {
687 calls_in_block(body, out);
688 }
689 if let Some(value) = &method.expression {
690 calls_in_expr(value, out);
691 }
692 }
693 csharp::Member::Field(field) => {
694 if let Some(value) = &field.value {
695 calls_in_expr(value, out);
696 }
697 }
698 csharp::Member::Property(property) => {
699 if let Some(value) = &property.expression {
700 calls_in_expr(value, out);
701 }
702 if let Some(value) = &property.initialiser {
703 calls_in_expr(value, out);
704 }
705 }
706 csharp::Member::Constructor(constructor) => calls_in_block(&constructor.body, out),
707 csharp::Member::Nested(_) | csharp::Member::Comment(_) | csharp::Member::Verbatim(_) => {}
708 }
709}
710
711fn calls_in_block(block: &csharp::Block, out: &mut Vec<String>) {
716 for statement in &block.statements {
717 calls_in_statement(statement, out);
718 }
719}
720
721fn calls_in_statement(statement: &csharp::Statement, out: &mut Vec<String>) {
726 match statement {
727 csharp::Statement::Declare { value, .. } => {
728 if let Some(value) = value {
729 calls_in_expr(value, out);
730 }
731 }
732 csharp::Statement::Expr(expr) | csharp::Statement::Throw(expr) => {
733 calls_in_expr(expr, out);
734 }
735 csharp::Statement::Return(value) => {
736 if let Some(value) = value {
737 calls_in_expr(value, out);
738 }
739 }
740 csharp::Statement::If {
741 condition,
742 then,
743 otherwise,
744 } => {
745 calls_in_expr(condition, out);
746 calls_in_block(then, out);
747 if let Some(otherwise) = otherwise {
748 calls_in_block(otherwise, out);
749 }
750 }
751 csharp::Statement::ForEach { range, body, .. } => {
752 calls_in_expr(range, out);
753 calls_in_block(body, out);
754 }
755 csharp::Statement::For {
756 init,
757 condition,
758 step,
759 body,
760 } => {
761 calls_in_statement(init, out);
762 calls_in_expr(condition, out);
763 calls_in_expr(step, out);
764 calls_in_block(body, out);
765 }
766 csharp::Statement::While { condition, body } => {
767 calls_in_expr(condition, out);
768 calls_in_block(body, out);
769 }
770 csharp::Statement::Switch { value, sections } => {
771 calls_in_expr(value, out);
772 for section in sections {
773 for label in §ion.labels {
774 calls_in_expr(label, out);
775 }
776 calls_in_block(§ion.body, out);
777 }
778 }
779 csharp::Statement::Using { resource, body } => {
780 calls_in_statement(resource, out);
781 calls_in_block(body, out);
782 }
783 csharp::Statement::Block(block) => calls_in_block(block, out),
784 csharp::Statement::Break | csharp::Statement::Comment(_) => {}
785 }
786}
787
788#[allow(clippy::too_many_lines)]
793fn calls_in_expr(expr: &csharp::Expr, out: &mut Vec<String>) {
794 match expr {
795 csharp::Expr::Invoke {
796 receiver,
797 name,
798 args,
799 } => {
800 if receiver.is_none() {
804 let name = name.as_str();
805 if name.starts_with("zd_def_") && !out.iter().any(|seen| seen == name) {
806 out.push(name.to_string());
807 }
808 }
809 if let Some(receiver) = receiver {
810 calls_in_expr(receiver, out);
811 }
812 for arg in args {
813 calls_in_expr(arg, out);
814 }
815 }
816 csharp::Expr::Member { value, .. }
817 | csharp::Expr::Cast { value, .. }
818 | csharp::Expr::Is { value, .. }
819 | csharp::Expr::Unary { value, .. }
820 | csharp::Expr::NameOf(value)
821 | csharp::Expr::Paren(value) => calls_in_expr(value, out),
822 csharp::Expr::Index { value, index } => {
823 calls_in_expr(value, out);
824 calls_in_expr(index, out);
825 }
826 csharp::Expr::New {
827 args, initialiser, ..
828 } => {
829 for arg in args {
830 calls_in_expr(arg, out);
831 }
832 for (_, value) in initialiser {
833 calls_in_expr(value, out);
834 }
835 }
836 csharp::Expr::NewArray {
837 length, elements, ..
838 } => {
839 if let Some(length) = length {
840 calls_in_expr(length, out);
841 }
842 for element in elements {
843 calls_in_expr(element, out);
844 }
845 }
846 csharp::Expr::Binary { lhs, rhs, .. } => {
847 calls_in_expr(lhs, out);
848 calls_in_expr(rhs, out);
849 }
850 csharp::Expr::Assign { target, value } => {
851 calls_in_expr(target, out);
852 calls_in_expr(value, out);
853 }
854 csharp::Expr::Conditional {
855 condition,
856 then,
857 otherwise,
858 } => {
859 calls_in_expr(condition, out);
860 calls_in_expr(then, out);
861 calls_in_expr(otherwise, out);
862 }
863 csharp::Expr::Lambda { body, .. } => calls_in_expr(body, out),
864 csharp::Expr::Literal(_)
865 | csharp::Expr::Path(_)
866 | csharp::Expr::This
867 | csharp::Expr::Default(_)
868 | csharp::Expr::TypeOf(_) => {}
869 }
870}
871
872fn path(text: &str) -> csharp::Expr {
875 csharp::Expr::Path(match text.strip_prefix("global::") {
876 Some(rest) => Path::rooted(&rest.split('.').collect::<Vec<_>>()),
877 None => Path::new(&text.split('.').collect::<Vec<_>>()),
878 })
879}
880
881fn name(text: &str) -> csharp::Expr {
882 path(text)
883}
884
885fn text(value: &str) -> csharp::Expr {
886 csharp::Expr::Literal(Literal::Str(value.to_string()))
887}
888
889fn call(callee: &str, args: Vec<csharp::Expr>) -> csharp::Expr {
890 csharp::Expr::Invoke {
891 receiver: None,
892 name: Ident::new(callee),
893 args,
894 }
895}
896
897fn member(base: &str, field: &str) -> csharp::Expr {
898 csharp::Expr::Member {
899 value: Box::new(name(base)),
900 name: Ident::new(field),
901 }
902}
903
904fn assign(place: csharp::Expr, value: csharp::Expr) -> csharp::Statement {
905 csharp::Statement::Expr(csharp::Expr::Binary {
906 op: "=",
907 lhs: Box::new(place),
908 rhs: Box::new(value),
909 })
910}
911
912fn give_back(value: csharp::Expr) -> csharp::Statement {
913 csharp::Statement::Return(Some(value))
914}
915
916fn new_local(type_: csharp::Type) -> csharp::Statement {
918 csharp::Statement::Declare {
919 type_: type_.clone(),
920 name: Ident::new("v"),
921 value: Some(csharp::Expr::New {
922 type_: Box::new(type_),
923 args: Vec::new(),
924 initialiser: Vec::new(),
925 }),
926 }
927}
928
929fn counted_loop(index: &str, count: csharp::Expr, body: csharp::Block) -> csharp::Statement {
930 csharp::Statement::For {
931 init: Box::new(csharp::Statement::Declare {
932 type_: csharp::Type::Predefined(csharp::Predefined::Int),
933 name: Ident::new(index),
934 value: Some(csharp::Expr::Literal(Literal::int(0))),
935 }),
936 condition: csharp::Expr::Binary {
937 op: "<",
938 lhs: Box::new(name(index)),
939 rhs: Box::new(count),
940 },
941 step: csharp::Expr::Binary {
942 op: "=",
943 lhs: Box::new(name(index)),
944 rhs: Box::new(csharp::Expr::Binary {
945 op: "+",
946 lhs: Box::new(name(index)),
947 rhs: Box::new(csharp::Expr::Literal(Literal::int(1))),
948 }),
949 },
950 body,
951 }
952}
953
954const _: Option<MaterialiseError> = None;