1use crate::lcnf::*;
6use std::fmt::Write as FmtWrite;
7
8use super::functions::CSHARP_RUNTIME;
9use super::functions::*;
10
11#[derive(Debug, Clone, PartialEq)]
13pub struct CSharpSwitchCase {
14 pub label: std::string::String,
16 pub stmts: Vec<CSharpStmt>,
17}
18#[derive(Debug, Clone, PartialEq)]
20pub enum CSharpStmt {
21 Expr(CSharpExpr),
23 Assign {
25 target: CSharpExpr,
26 value: CSharpExpr,
27 },
28 LocalVar {
30 name: std::string::String,
31 ty: Option<CSharpType>,
32 init: Option<CSharpExpr>,
33 is_const: bool,
34 },
35 If {
37 cond: CSharpExpr,
38 then_stmts: Vec<CSharpStmt>,
39 else_stmts: Vec<CSharpStmt>,
40 },
41 Switch {
43 expr: CSharpExpr,
44 cases: Vec<CSharpSwitchCase>,
45 default: Vec<CSharpStmt>,
46 },
47 While {
49 cond: CSharpExpr,
50 body: Vec<CSharpStmt>,
51 },
52 For {
54 init: Option<Box<CSharpStmt>>,
55 cond: Option<CSharpExpr>,
56 step: Option<CSharpExpr>,
57 body: Vec<CSharpStmt>,
58 },
59 ForEach {
61 var_name: std::string::String,
62 var_ty: Option<CSharpType>,
63 collection: CSharpExpr,
64 body: Vec<CSharpStmt>,
65 },
66 Return(Option<CSharpExpr>),
68 Throw(CSharpExpr),
70 TryCatch {
72 try_stmts: Vec<CSharpStmt>,
73 catches: Vec<CSharpCatchClause>,
74 finally_stmts: Vec<CSharpStmt>,
75 },
76 Using {
78 resource: CSharpExpr,
79 var_name: Option<std::string::String>,
80 body: Vec<CSharpStmt>,
81 },
82 Lock {
84 obj: CSharpExpr,
85 body: Vec<CSharpStmt>,
86 },
87 Break,
89 Continue,
91 YieldReturn(CSharpExpr),
93 YieldBreak,
95}
96#[derive(Debug, Clone, PartialEq)]
98pub struct CSharpInterface {
99 pub name: std::string::String,
100 pub methods: Vec<CSharpMethod>,
102 pub properties: Vec<CSharpProperty>,
104 pub extends: Vec<std::string::String>,
106 pub visibility: CSharpVisibility,
107 pub type_params: Vec<std::string::String>,
108}
109impl CSharpInterface {
110 pub fn new(name: &str) -> Self {
111 CSharpInterface {
112 name: name.to_string(),
113 methods: Vec::new(),
114 properties: Vec::new(),
115 extends: Vec::new(),
116 visibility: CSharpVisibility::Public,
117 type_params: Vec::new(),
118 }
119 }
120 pub fn emit(&self, indent: &str) -> std::string::String {
121 let inner = format!("{} ", indent);
122 let mut out = std::string::String::new();
123 let type_params_str = if self.type_params.is_empty() {
124 std::string::String::new()
125 } else {
126 format!("<{}>", self.type_params.join(", "))
127 };
128 let extends_str = if self.extends.is_empty() {
129 std::string::String::new()
130 } else {
131 format!(" : {}", self.extends.join(", "))
132 };
133 let _ = writeln!(
134 out,
135 "{}{} interface {}{}{}",
136 indent, self.visibility, self.name, type_params_str, extends_str
137 );
138 let _ = writeln!(out, "{}{{", indent);
139 for prop in &self.properties {
140 out.push_str(&prop.emit(&inner));
141 }
142 for method in &self.methods {
143 let mut iface_method = method.clone();
146 iface_method.is_abstract = false;
147 if method.body.is_empty() && method.expr_body.is_none() {
148 let params_str = method
150 .params
151 .iter()
152 .map(|(n, t)| format!("{} {}", t, n))
153 .collect::<Vec<_>>()
154 .join(", ");
155 let type_params_str = if method.type_params.is_empty() {
156 std::string::String::new()
157 } else {
158 format!("<{}>", method.type_params.join(", "))
159 };
160 let _ = writeln!(
161 out,
162 "{}{} {} {}{}({});",
163 inner,
164 method.visibility,
165 method.return_type,
166 method.name,
167 type_params_str,
168 params_str
169 );
170 } else {
171 out.push_str(&iface_method.emit(&inner));
172 }
173 }
174 let _ = writeln!(out, "{}}}", indent);
175 out
176 }
177}
178#[derive(Debug, Clone, PartialEq)]
180pub struct CSharpField {
181 pub name: std::string::String,
182 pub ty: CSharpType,
183 pub visibility: CSharpVisibility,
184 pub is_static: bool,
185 pub is_readonly: bool,
186 pub is_const: bool,
187 pub default_value: Option<CSharpExpr>,
188}
189impl CSharpField {
190 pub fn new(name: &str, ty: CSharpType) -> Self {
191 CSharpField {
192 name: name.to_string(),
193 ty,
194 visibility: CSharpVisibility::Private,
195 is_static: false,
196 is_readonly: false,
197 is_const: false,
198 default_value: None,
199 }
200 }
201 pub fn emit(&self, indent: &str) -> std::string::String {
202 let mut out = std::string::String::new();
203 let mut mods = vec![format!("{}", self.visibility)];
204 if self.is_static {
205 mods.push("static".to_string());
206 }
207 if self.is_const {
208 mods.push("const".to_string());
209 }
210 if self.is_readonly {
211 mods.push("readonly".to_string());
212 }
213 if let Some(val) = &self.default_value {
214 let _ = writeln!(
215 out,
216 "{}{} {} {} = {};",
217 indent,
218 mods.join(" "),
219 self.ty,
220 self.name,
221 val
222 );
223 } else {
224 let _ = writeln!(
225 out,
226 "{}{} {} {};",
227 indent,
228 mods.join(" "),
229 self.ty,
230 self.name
231 );
232 }
233 out
234 }
235}
236#[derive(Debug, Clone, PartialEq)]
238pub struct CSharpMethod {
239 pub name: std::string::String,
241 pub return_type: CSharpType,
243 pub params: Vec<(std::string::String, CSharpType)>,
245 pub body: Vec<CSharpStmt>,
247 pub visibility: CSharpVisibility,
249 pub is_static: bool,
251 pub is_async: bool,
253 pub is_override: bool,
255 pub is_virtual: bool,
257 pub is_abstract: bool,
259 pub type_params: Vec<std::string::String>,
261 pub expr_body: Option<CSharpExpr>,
263}
264impl CSharpMethod {
265 pub fn new(name: &str, return_type: CSharpType) -> Self {
267 CSharpMethod {
268 name: name.to_string(),
269 return_type,
270 params: Vec::new(),
271 body: Vec::new(),
272 visibility: CSharpVisibility::Public,
273 is_static: false,
274 is_async: false,
275 is_override: false,
276 is_virtual: false,
277 is_abstract: false,
278 type_params: Vec::new(),
279 expr_body: None,
280 }
281 }
282 pub fn emit(&self, indent: &str) -> std::string::String {
284 let inner = format!("{} ", indent);
285 let mut out = std::string::String::new();
286 let mut mods = vec![format!("{}", self.visibility)];
287 if self.is_static {
288 mods.push("static".to_string());
289 }
290 if self.is_async {
291 mods.push("async".to_string());
292 }
293 if self.is_abstract {
294 mods.push("abstract".to_string());
295 }
296 if self.is_override {
297 mods.push("override".to_string());
298 }
299 if self.is_virtual {
300 mods.push("virtual".to_string());
301 }
302 let type_params_str = if self.type_params.is_empty() {
303 std::string::String::new()
304 } else {
305 format!("<{}>", self.type_params.join(", "))
306 };
307 let params_str = self
308 .params
309 .iter()
310 .map(|(n, t)| format!("{} {}", t, n))
311 .collect::<Vec<_>>()
312 .join(", ");
313 let _ = write!(
314 out,
315 "{}{} {} {}{}({})",
316 indent,
317 mods.join(" "),
318 self.return_type,
319 self.name,
320 type_params_str,
321 params_str
322 );
323 if self.is_abstract {
324 let _ = writeln!(out, ";");
325 return out;
326 }
327 if let Some(expr) = &self.expr_body {
328 let _ = writeln!(out, " => {};", expr);
329 return out;
330 }
331 let _ = writeln!(out);
332 let _ = writeln!(out, "{}{{", indent);
333 emit_stmts(&self.body, &inner, &mut out);
334 let _ = writeln!(out, "{}}}", indent);
335 out
336 }
337}
338#[derive(Debug, Clone)]
340pub struct CSharpModule {
341 pub namespace: std::string::String,
343 pub using_directives: Vec<std::string::String>,
345 pub classes: Vec<CSharpClass>,
347 pub records: Vec<CSharpRecord>,
349 pub interfaces: Vec<CSharpInterface>,
351 pub enums: Vec<CSharpEnum>,
353 pub header_comment: Option<std::string::String>,
355 pub nullable_enable: bool,
357}
358impl CSharpModule {
359 pub fn new(namespace: &str) -> Self {
361 CSharpModule {
362 namespace: namespace.to_string(),
363 using_directives: Vec::new(),
364 classes: Vec::new(),
365 records: Vec::new(),
366 interfaces: Vec::new(),
367 enums: Vec::new(),
368 header_comment: None,
369 nullable_enable: true,
370 }
371 }
372 pub fn add_using(&mut self, ns: &str) {
374 if !self.using_directives.iter().any(|u| u == ns) {
375 self.using_directives.push(ns.to_string());
376 }
377 }
378 pub fn emit(&self) -> std::string::String {
380 let mut out = std::string::String::new();
381 if let Some(comment) = &self.header_comment {
382 for line in comment.lines() {
383 let _ = writeln!(out, "// {}", line);
384 }
385 let _ = writeln!(out);
386 }
387 if self.nullable_enable {
388 let _ = writeln!(out, "#nullable enable");
389 let _ = writeln!(out);
390 }
391 let mut usings = self.using_directives.clone();
392 usings.sort();
393 usings.dedup();
394 for u in &usings {
395 let _ = writeln!(out, "using {};", u);
396 }
397 if !usings.is_empty() {
398 let _ = writeln!(out);
399 }
400 let _ = writeln!(out, "namespace {};", self.namespace);
401 let _ = writeln!(out);
402 for e in &self.enums {
403 out.push_str(&e.emit(""));
404 let _ = writeln!(out);
405 }
406 for iface in &self.interfaces {
407 out.push_str(&iface.emit(""));
408 let _ = writeln!(out);
409 }
410 for rec in &self.records {
411 out.push_str(&rec.emit(""));
412 let _ = writeln!(out);
413 }
414 for cls in &self.classes {
415 out.push_str(&cls.emit(""));
416 let _ = writeln!(out);
417 }
418 out.push_str(CSHARP_RUNTIME);
419 out
420 }
421}
422#[derive(Debug, Clone, PartialEq)]
424pub struct CSharpConstructor {
425 pub class_name: std::string::String,
426 pub params: Vec<(std::string::String, CSharpType)>,
427 pub body: Vec<CSharpStmt>,
428 pub visibility: CSharpVisibility,
429 pub base_call: Option<(bool, Vec<CSharpExpr>)>,
431}
432impl CSharpConstructor {
433 pub fn new(class_name: &str) -> Self {
434 CSharpConstructor {
435 class_name: class_name.to_string(),
436 params: Vec::new(),
437 body: Vec::new(),
438 visibility: CSharpVisibility::Public,
439 base_call: None,
440 }
441 }
442 pub fn emit(&self, indent: &str) -> std::string::String {
443 let inner = format!("{} ", indent);
444 let mut out = std::string::String::new();
445 let params_str = self
446 .params
447 .iter()
448 .map(|(n, t)| format!("{} {}", t, n))
449 .collect::<Vec<_>>()
450 .join(", ");
451 let base_str = match &self.base_call {
452 None => std::string::String::new(),
453 Some((is_base, args)) => {
454 let kw = if *is_base { "base" } else { "this" };
455 let args_str = args
456 .iter()
457 .map(|a| format!("{}", a))
458 .collect::<Vec<_>>()
459 .join(", ");
460 format!(" : {}({})", kw, args_str)
461 }
462 };
463 let _ = writeln!(
464 out,
465 "{}{} {}({}){}",
466 indent, self.visibility, self.class_name, params_str, base_str
467 );
468 let _ = writeln!(out, "{}{{", indent);
469 emit_stmts(&self.body, &inner, &mut out);
470 let _ = writeln!(out, "{}}}", indent);
471 out
472 }
473}
474#[derive(Debug, Clone, PartialEq)]
476pub enum CSharpInterpolationPart {
477 Text(std::string::String),
479 Expr(CSharpExpr),
481 ExprFmt(CSharpExpr, std::string::String),
483}
484#[derive(Debug, Clone, PartialEq)]
486pub struct CSharpClass {
487 pub name: std::string::String,
488 pub base_class: Option<std::string::String>,
490 pub interfaces: Vec<std::string::String>,
492 pub methods: Vec<CSharpMethod>,
494 pub properties: Vec<CSharpProperty>,
496 pub constructors: Vec<CSharpConstructor>,
498 pub is_sealed: bool,
500 pub is_abstract: bool,
502 pub is_static: bool,
504 pub is_partial: bool,
506 pub visibility: CSharpVisibility,
507 pub type_params: Vec<std::string::String>,
508 pub fields: Vec<CSharpField>,
510}
511impl CSharpClass {
512 pub fn new(name: &str) -> Self {
513 CSharpClass {
514 name: name.to_string(),
515 base_class: None,
516 interfaces: Vec::new(),
517 methods: Vec::new(),
518 properties: Vec::new(),
519 constructors: Vec::new(),
520 is_sealed: false,
521 is_abstract: false,
522 is_static: false,
523 is_partial: false,
524 visibility: CSharpVisibility::Public,
525 type_params: Vec::new(),
526 fields: Vec::new(),
527 }
528 }
529 pub fn emit(&self, indent: &str) -> std::string::String {
531 let inner = format!("{} ", indent);
532 let mut out = std::string::String::new();
533 let mut mods = vec![format!("{}", self.visibility)];
534 if self.is_static {
535 mods.push("static".to_string());
536 }
537 if self.is_sealed {
538 mods.push("sealed".to_string());
539 }
540 if self.is_abstract {
541 mods.push("abstract".to_string());
542 }
543 if self.is_partial {
544 mods.push("partial".to_string());
545 }
546 let type_params_str = if self.type_params.is_empty() {
547 std::string::String::new()
548 } else {
549 format!("<{}>", self.type_params.join(", "))
550 };
551 let mut inherits: Vec<std::string::String> = Vec::new();
552 if let Some(base) = &self.base_class {
553 inherits.push(base.clone());
554 }
555 inherits.extend(self.interfaces.iter().cloned());
556 let inherit_str = if inherits.is_empty() {
557 std::string::String::new()
558 } else {
559 format!(" : {}", inherits.join(", "))
560 };
561 let _ = writeln!(
562 out,
563 "{}{} class {}{}{}",
564 indent,
565 mods.join(" "),
566 self.name,
567 type_params_str,
568 inherit_str
569 );
570 let _ = writeln!(out, "{}{{", indent);
571 for field in &self.fields {
572 out.push_str(&field.emit(&inner));
573 }
574 for ctor in &self.constructors {
575 out.push_str(&ctor.emit(&inner));
576 }
577 for prop in &self.properties {
578 out.push_str(&prop.emit(&inner));
579 }
580 for method in &self.methods {
581 out.push_str(&method.emit(&inner));
582 }
583 let _ = writeln!(out, "{}}}", indent);
584 out
585 }
586}
587#[derive(Debug, Clone, PartialEq)]
589pub enum CSharpExpr {
590 Lit(CSharpLit),
592 Var(std::string::String),
594 BinOp {
596 op: std::string::String,
597 lhs: Box<CSharpExpr>,
598 rhs: Box<CSharpExpr>,
599 },
600 UnaryOp {
602 op: std::string::String,
603 operand: Box<CSharpExpr>,
604 },
605 Call {
607 callee: Box<CSharpExpr>,
608 args: Vec<CSharpExpr>,
609 },
610 MethodCall {
612 receiver: Box<CSharpExpr>,
613 method: std::string::String,
614 type_args: Vec<CSharpType>,
615 args: Vec<CSharpExpr>,
616 },
617 New {
619 ty: CSharpType,
620 args: Vec<CSharpExpr>,
621 },
622 Lambda {
624 params: Vec<(std::string::String, Option<CSharpType>)>,
625 body: Box<CSharpExpr>,
626 },
627 Ternary {
629 cond: Box<CSharpExpr>,
630 then_expr: Box<CSharpExpr>,
631 else_expr: Box<CSharpExpr>,
632 },
633 Null,
635 Default(Option<CSharpType>),
637 NameOf(std::string::String),
639 TypeOf(CSharpType),
641 Await(Box<CSharpExpr>),
643 Throw(Box<CSharpExpr>),
645 Is {
647 expr: Box<CSharpExpr>,
648 pattern: std::string::String,
649 },
650 As {
652 expr: Box<CSharpExpr>,
653 ty: CSharpType,
654 },
655 Member(Box<CSharpExpr>, std::string::String),
657 Index(Box<CSharpExpr>, Box<CSharpExpr>),
659 SwitchExpr {
661 scrutinee: Box<CSharpExpr>,
662 arms: Vec<CSharpSwitchArm>,
663 },
664 Interpolated(Vec<CSharpInterpolationPart>),
666 CollectionExpr(Vec<CSharpExpr>),
668}
669#[derive(Debug, Clone, PartialEq)]
671pub struct CSharpProperty {
672 pub name: std::string::String,
673 pub ty: CSharpType,
674 pub visibility: CSharpVisibility,
675 pub has_getter: bool,
676 pub has_setter: bool,
677 pub is_init_only: bool,
678 pub is_static: bool,
679 pub default_value: Option<CSharpExpr>,
680 pub expr_body: Option<CSharpExpr>,
682}
683impl CSharpProperty {
684 pub fn new_auto(name: &str, ty: CSharpType) -> Self {
685 CSharpProperty {
686 name: name.to_string(),
687 ty,
688 visibility: CSharpVisibility::Public,
689 has_getter: true,
690 has_setter: true,
691 is_init_only: false,
692 is_static: false,
693 default_value: None,
694 expr_body: None,
695 }
696 }
697 pub fn emit(&self, indent: &str) -> std::string::String {
698 let mut out = std::string::String::new();
699 let mut mods = vec![format!("{}", self.visibility)];
700 if self.is_static {
701 mods.push("static".to_string());
702 }
703 if let Some(expr) = &self.expr_body {
704 let _ = writeln!(
705 out,
706 "{}{} {} {} => {};",
707 indent,
708 mods.join(" "),
709 self.ty,
710 self.name,
711 expr
712 );
713 return out;
714 }
715 let accessors = match (self.has_getter, self.has_setter, self.is_init_only) {
716 (true, true, false) => "{ get; set; }",
717 (true, false, false) => "{ get; }",
718 (true, _, true) => "{ get; init; }",
719 (false, true, false) => "{ set; }",
720 _ => "{ get; set; }",
721 };
722 if let Some(val) = &self.default_value {
723 let _ = writeln!(
724 out,
725 "{}{} {} {} {} = {};",
726 indent,
727 mods.join(" "),
728 self.ty,
729 self.name,
730 accessors,
731 val
732 );
733 } else {
734 let _ = writeln!(
735 out,
736 "{}{} {} {} {};",
737 indent,
738 mods.join(" "),
739 self.ty,
740 self.name,
741 accessors
742 );
743 }
744 out
745 }
746}
747#[derive(Debug, Clone, PartialEq)]
749pub struct CSharpEnum {
750 pub name: std::string::String,
751 pub variants: Vec<(std::string::String, Option<i64>)>,
752 pub visibility: CSharpVisibility,
753 pub underlying_type: Option<CSharpType>,
754}
755impl CSharpEnum {
756 pub fn new(name: &str) -> Self {
757 CSharpEnum {
758 name: name.to_string(),
759 variants: Vec::new(),
760 visibility: CSharpVisibility::Public,
761 underlying_type: None,
762 }
763 }
764 pub fn emit(&self, indent: &str) -> std::string::String {
765 let inner = format!("{} ", indent);
766 let mut out = std::string::String::new();
767 let base_str = match &self.underlying_type {
768 None => std::string::String::new(),
769 Some(t) => format!(" : {}", t),
770 };
771 let _ = writeln!(
772 out,
773 "{}{} enum {}{}",
774 indent, self.visibility, self.name, base_str
775 );
776 let _ = writeln!(out, "{}{{", indent);
777 for (name, val) in &self.variants {
778 if let Some(v) = val {
779 let _ = writeln!(out, "{}{} = {},", inner, name, v);
780 } else {
781 let _ = writeln!(out, "{}{},", inner, name);
782 }
783 }
784 let _ = writeln!(out, "{}}}", indent);
785 out
786 }
787}
788#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
790pub enum CSharpVisibility {
791 Public,
792 Private,
793 Protected,
794 Internal,
795 ProtectedInternal,
796 PrivateProtected,
797}
798#[derive(Debug, Clone, PartialEq, Eq, Hash)]
800pub enum CSharpType {
801 Int,
803 Long,
805 Double,
807 Float,
809 Bool,
811 String,
813 Void,
815 Object,
817 List(Box<CSharpType>),
819 Dict(Box<CSharpType>, Box<CSharpType>),
821 Tuple(Vec<CSharpType>),
823 Custom(std::string::String),
825 Nullable(Box<CSharpType>),
827 Task(Box<CSharpType>),
829 IEnumerable(Box<CSharpType>),
831 Func(Vec<CSharpType>, Box<CSharpType>),
833 Action(Vec<CSharpType>),
835}
836#[derive(Debug, Clone, PartialEq)]
838pub enum CSharpLit {
839 Int(i64),
841 Long(i64),
843 Bool(bool),
845 Str(std::string::String),
847 Null,
849 Float(f64),
851 Double(f64),
853 Char(char),
855}
856#[derive(Debug, Clone, PartialEq)]
858pub struct CSharpCatchClause {
859 pub exception_type: CSharpType,
861 pub var_name: std::string::String,
863 pub stmts: Vec<CSharpStmt>,
865}
866#[derive(Debug, Clone, PartialEq)]
869pub struct CSharpRecord {
870 pub name: std::string::String,
871 pub fields: Vec<(std::string::String, CSharpType)>,
873 pub methods: Vec<CSharpMethod>,
875 pub is_readonly: bool,
877 pub is_sealed: bool,
879 pub base_record: Option<std::string::String>,
881 pub interfaces: Vec<std::string::String>,
883 pub visibility: CSharpVisibility,
884}
885impl CSharpRecord {
886 pub fn new(name: &str) -> Self {
887 CSharpRecord {
888 name: name.to_string(),
889 fields: Vec::new(),
890 methods: Vec::new(),
891 is_readonly: false,
892 is_sealed: false,
893 base_record: None,
894 interfaces: Vec::new(),
895 visibility: CSharpVisibility::Public,
896 }
897 }
898 pub fn emit(&self, indent: &str) -> std::string::String {
900 let inner = format!("{} ", indent);
901 let mut out = std::string::String::new();
902 let mut mods = vec![format!("{}", self.visibility)];
903 if self.is_sealed {
904 mods.push("sealed".to_string());
905 }
906 if self.is_readonly {
907 mods.push("readonly".to_string());
908 }
909 let fields_str = self
910 .fields
911 .iter()
912 .map(|(n, t)| format!("{} {}", t, n))
913 .collect::<Vec<_>>()
914 .join(", ");
915 let mut inherits: Vec<std::string::String> = Vec::new();
916 if let Some(base) = &self.base_record {
917 inherits.push(base.clone());
918 }
919 inherits.extend(self.interfaces.iter().cloned());
920 let inherit_str = if inherits.is_empty() {
921 std::string::String::new()
922 } else {
923 format!(" : {}", inherits.join(", "))
924 };
925 let record_kw = if self.is_readonly {
926 "record struct"
927 } else {
928 "record"
929 };
930 if self.methods.is_empty() {
931 let _ = writeln!(
932 out,
933 "{}{} {} {}({}){}",
934 indent,
935 mods.join(" "),
936 record_kw,
937 self.name,
938 fields_str,
939 inherit_str
940 );
941 if out.ends_with('\n') {
942 out.pop();
943 out.push(';');
944 out.push('\n');
945 }
946 } else {
947 let _ = writeln!(
948 out,
949 "{}{} {} {}({}){}",
950 indent,
951 mods.join(" "),
952 record_kw,
953 self.name,
954 fields_str,
955 inherit_str
956 );
957 let _ = writeln!(out, "{}{{", indent);
958 for method in &self.methods {
959 out.push_str(&method.emit(&inner));
960 }
961 let _ = writeln!(out, "{}}}", indent);
962 }
963 out
964 }
965}
966pub struct CSharpBackend {
970 pub emit_public: bool,
972 pub emit_comments: bool,
974 pub(super) var_counter: u64,
976 pub prefer_async: bool,
978}
979impl CSharpBackend {
980 pub fn new() -> Self {
982 CSharpBackend {
983 emit_public: true,
984 emit_comments: true,
985 var_counter: 0,
986 prefer_async: false,
987 }
988 }
989 pub fn fresh_var(&mut self) -> std::string::String {
991 let n = self.var_counter;
992 self.var_counter += 1;
993 format!("_cs{}", n)
994 }
995 pub fn mangle_name(name: &str) -> std::string::String {
997 if name.is_empty() {
998 return "ox_empty".to_string();
999 }
1000 let mangled: std::string::String = name
1001 .chars()
1002 .map(|c| {
1003 if c.is_alphanumeric() || c == '_' {
1004 c
1005 } else {
1006 '_'
1007 }
1008 })
1009 .collect();
1010 if mangled
1011 .chars()
1012 .next()
1013 .map(|c| c.is_ascii_digit())
1014 .unwrap_or(false)
1015 || is_csharp_keyword(&mangled)
1016 {
1017 format!("ox_{}", mangled)
1018 } else {
1019 mangled
1020 }
1021 }
1022 pub fn compile_decl(&self, decl: &LcnfFunDecl) -> CSharpMethod {
1024 let ret_ty = lcnf_type_to_csharp(&decl.ret_type);
1025 let mut method = CSharpMethod::new(&Self::mangle_name(&decl.name), ret_ty);
1026 method.visibility = if self.emit_public {
1027 CSharpVisibility::Public
1028 } else {
1029 CSharpVisibility::Private
1030 };
1031 method.is_static = true;
1032 for param in &decl.params {
1033 if param.erased {
1034 continue;
1035 }
1036 let param_ty = lcnf_type_to_csharp(¶m.ty);
1037 let param_name = format!("_x{}", param.id.0);
1038 method.params.push((param_name, param_ty));
1039 }
1040 let mut stmts: Vec<CSharpStmt> = Vec::new();
1041 let result = self.compile_expr_to_stmts(&decl.body, &mut stmts);
1042 stmts.push(CSharpStmt::Return(Some(result)));
1043 method.body = stmts;
1044 method
1045 }
1046 pub(super) fn compile_expr_to_stmts(
1049 &self,
1050 expr: &LcnfExpr,
1051 stmts: &mut Vec<CSharpStmt>,
1052 ) -> CSharpExpr {
1053 match expr {
1054 LcnfExpr::Return(arg) => self.compile_arg(arg),
1055 LcnfExpr::Unreachable => CSharpExpr::Throw(Box::new(CSharpExpr::New {
1056 ty: CSharpType::Custom("InvalidOperationException".to_string()),
1057 args: vec![CSharpExpr::Lit(CSharpLit::Str(
1058 "OxiLean: unreachable code reached".to_string(),
1059 ))],
1060 })),
1061 LcnfExpr::TailCall(func, args) => {
1062 let callee = self.compile_arg(func);
1063 let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1064 CSharpExpr::Call {
1065 callee: Box::new(callee),
1066 args: cs_args,
1067 }
1068 }
1069 LcnfExpr::Let {
1070 id, value, body, ..
1071 } => {
1072 let val_expr = self.compile_let_value(value);
1073 let var_name = format!("_x{}", id.0);
1074 stmts.push(CSharpStmt::LocalVar {
1075 name: var_name,
1076 ty: None,
1077 init: Some(val_expr),
1078 is_const: false,
1079 });
1080 self.compile_expr_to_stmts(body, stmts)
1081 }
1082 LcnfExpr::Case {
1083 scrutinee,
1084 alts,
1085 default,
1086 ..
1087 } => {
1088 let scrutinee_expr = CSharpExpr::Var(format!("_x{}", scrutinee.0));
1089 let tag_expr =
1090 CSharpExpr::Member(Box::new(scrutinee_expr.clone()), "Tag".to_string());
1091 let result_var = format!("_cs_case{}", stmts.len());
1092 stmts.push(CSharpStmt::LocalVar {
1093 name: result_var.clone(),
1094 ty: Some(CSharpType::Object),
1095 init: Some(CSharpExpr::Null),
1096 is_const: false,
1097 });
1098 let mut cases: Vec<CSharpSwitchCase> = Vec::new();
1099 for alt in alts {
1100 let mut branch_stmts: Vec<CSharpStmt> = Vec::new();
1101 for (field_idx, param) in alt.params.iter().enumerate() {
1102 if param.erased {
1103 continue;
1104 }
1105 let field_access = CSharpExpr::Member(
1106 Box::new(scrutinee_expr.clone()),
1107 format!("Field{}", field_idx),
1108 );
1109 branch_stmts.push(CSharpStmt::LocalVar {
1110 name: format!("_x{}", param.id.0),
1111 ty: Some(lcnf_type_to_csharp(¶m.ty)),
1112 init: Some(field_access),
1113 is_const: false,
1114 });
1115 }
1116 let branch_result = self.compile_expr_to_stmts(&alt.body, &mut branch_stmts);
1117 branch_stmts.push(CSharpStmt::Assign {
1118 target: CSharpExpr::Var(result_var.clone()),
1119 value: branch_result,
1120 });
1121 branch_stmts.push(CSharpStmt::Break);
1122 cases.push(CSharpSwitchCase {
1123 label: format!("{}", alt.ctor_tag),
1124 stmts: branch_stmts,
1125 });
1126 }
1127 let mut default_stmts: Vec<CSharpStmt> = Vec::new();
1128 if let Some(def) = default {
1129 let def_result = self.compile_expr_to_stmts(def, &mut default_stmts);
1130 default_stmts.push(CSharpStmt::Assign {
1131 target: CSharpExpr::Var(result_var.clone()),
1132 value: def_result,
1133 });
1134 } else {
1135 default_stmts.push(CSharpStmt::Throw(CSharpExpr::New {
1136 ty: CSharpType::Custom("InvalidOperationException".to_string()),
1137 args: vec![CSharpExpr::Lit(CSharpLit::Str(
1138 "OxiLean: unreachable case".to_string(),
1139 ))],
1140 }));
1141 }
1142 stmts.push(CSharpStmt::Switch {
1143 expr: tag_expr,
1144 cases,
1145 default: default_stmts,
1146 });
1147 CSharpExpr::Var(result_var)
1148 }
1149 }
1150 }
1151 pub(super) fn compile_let_value(&self, value: &LcnfLetValue) -> CSharpExpr {
1153 match value {
1154 LcnfLetValue::Lit(lit) => self.compile_lit(lit),
1155 LcnfLetValue::Erased => CSharpExpr::Null,
1156 LcnfLetValue::FVar(id) => CSharpExpr::Var(format!("_x{}", id.0)),
1157 LcnfLetValue::App(func, args) => {
1158 let callee = self.compile_arg(func);
1159 let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1160 CSharpExpr::Call {
1161 callee: Box::new(callee),
1162 args: cs_args,
1163 }
1164 }
1165 LcnfLetValue::Proj(_name, idx, var) => {
1166 let base = CSharpExpr::Var(format!("_x{}", var.0));
1167 CSharpExpr::Member(Box::new(base), format!("Field{}", idx))
1168 }
1169 LcnfLetValue::Ctor(name, _tag, args) => {
1170 let ctor_name = Self::mangle_name(name);
1171 let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1172 CSharpExpr::New {
1173 ty: CSharpType::Custom(ctor_name),
1174 args: cs_args,
1175 }
1176 }
1177 LcnfLetValue::Reset(_var) => CSharpExpr::Null,
1178 LcnfLetValue::Reuse(_slot, name, _tag, args) => {
1179 let ctor_name = Self::mangle_name(name);
1180 let cs_args: Vec<CSharpExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1181 CSharpExpr::New {
1182 ty: CSharpType::Custom(ctor_name),
1183 args: cs_args,
1184 }
1185 }
1186 }
1187 }
1188 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> CSharpExpr {
1190 match arg {
1191 LcnfArg::Var(id) => CSharpExpr::Var(format!("_x{}", id.0)),
1192 LcnfArg::Lit(lit) => self.compile_lit(lit),
1193 LcnfArg::Erased => CSharpExpr::Null,
1194 LcnfArg::Type(_) => CSharpExpr::Null,
1195 }
1196 }
1197 pub(super) fn compile_lit(&self, lit: &LcnfLit) -> CSharpExpr {
1199 match lit {
1200 LcnfLit::Nat(n) => CSharpExpr::Lit(CSharpLit::Long(*n as i64)),
1201 LcnfLit::Str(s) => CSharpExpr::Lit(CSharpLit::Str(s.clone())),
1202 }
1203 }
1204 pub fn emit_module(&self, namespace: &str, decls: &[LcnfFunDecl]) -> CSharpModule {
1206 let mut module = CSharpModule::new(namespace);
1207 module.header_comment = Some(format!(
1208 "OxiLean-generated C# module: {}\nGenerated by OxiLean CSharpBackend",
1209 namespace
1210 ));
1211 module.add_using("System");
1212 module.add_using("System.Collections.Generic");
1213 module.add_using("System.Linq");
1214 module.add_using("System.Threading.Tasks");
1215 let mut runtime_class = CSharpClass::new("OxiLeanRuntime");
1216 runtime_class.is_static = true;
1217 runtime_class.visibility = CSharpVisibility::Internal;
1218 for decl in decls {
1219 let method = self.compile_decl(decl);
1220 runtime_class.methods.push(method);
1221 }
1222 module.classes.push(runtime_class);
1223 module
1224 }
1225}
1226#[derive(Debug, Clone, PartialEq)]
1228pub struct CSharpSwitchArm {
1229 pub pattern: std::string::String,
1231 pub guard: Option<CSharpExpr>,
1233 pub body: CSharpExpr,
1235}