1use super::functions::*;
6
7#[derive(Debug, Clone)]
9pub enum FSharpExpr {
10 Lit(String),
12 Var(String),
14 App(Box<FSharpExpr>, Box<FSharpExpr>),
16 Lambda(String, Box<FSharpExpr>),
18 MultiLambda(Vec<String>, Box<FSharpExpr>),
20 Let(String, Box<FSharpExpr>, Box<FSharpExpr>),
22 LetRec(String, Box<FSharpExpr>, Box<FSharpExpr>),
24 Match(Box<FSharpExpr>, Vec<(FSharpPattern, FSharpExpr)>),
26 If(Box<FSharpExpr>, Box<FSharpExpr>, Box<FSharpExpr>),
28 Tuple(Vec<FSharpExpr>),
30 FsList(Vec<FSharpExpr>),
32 FsArray(Vec<FSharpExpr>),
34 BinOp(String, Box<FSharpExpr>, Box<FSharpExpr>),
36 UnaryOp(String, Box<FSharpExpr>),
38 Record(Vec<(String, FSharpExpr)>),
40 RecordUpdate(Box<FSharpExpr>, Vec<(String, FSharpExpr)>),
42 FieldAccess(Box<FSharpExpr>, String),
44 Seq(Box<FSharpExpr>, Box<FSharpExpr>),
46 Ann(Box<FSharpExpr>, FSharpType),
48 Pipe(Box<FSharpExpr>, Box<FSharpExpr>),
50 Ctor(String, Vec<FSharpExpr>),
52 Do(Vec<FSharpExpr>),
54 Raw(String),
56}
57#[derive(Debug, Clone)]
59pub struct FSharpUnion {
60 pub name: String,
62 pub type_params: Vec<String>,
64 pub cases: Vec<FSharpUnionCase>,
66 pub doc: Option<String>,
68}
69#[allow(dead_code)]
71pub struct FSharpFunctionBuilder {
72 pub(super) func: FSharpFunction,
73}
74impl FSharpFunctionBuilder {
75 #[allow(dead_code)]
77 pub fn new(name: impl Into<String>) -> Self {
78 FSharpFunctionBuilder {
79 func: FSharpFunction {
80 name: name.into(),
81 is_recursive: false,
82 is_inline: false,
83 type_params: vec![],
84 params: vec![],
85 return_type: None,
86 body: funit(),
87 doc: None,
88 },
89 }
90 }
91 #[allow(dead_code)]
93 pub fn recursive(mut self) -> Self {
94 self.func.is_recursive = true;
95 self
96 }
97 #[allow(dead_code)]
99 pub fn inline(mut self) -> Self {
100 self.func.is_inline = true;
101 self
102 }
103 #[allow(dead_code)]
105 pub fn type_param(mut self, tp: impl Into<String>) -> Self {
106 self.func.type_params.push(tp.into());
107 self
108 }
109 #[allow(dead_code)]
111 pub fn param(mut self, name: impl Into<String>, ty: Option<FSharpType>) -> Self {
112 self.func.params.push((name.into(), ty));
113 self
114 }
115 #[allow(dead_code)]
117 pub fn returns(mut self, ty: FSharpType) -> Self {
118 self.func.return_type = Some(ty);
119 self
120 }
121 #[allow(dead_code)]
123 pub fn body(mut self, expr: FSharpExpr) -> Self {
124 self.func.body = expr;
125 self
126 }
127 #[allow(dead_code)]
129 pub fn doc(mut self, doc: impl Into<String>) -> Self {
130 self.func.doc = Some(doc.into());
131 self
132 }
133 #[allow(dead_code)]
135 pub fn build(self) -> FSharpFunction {
136 self.func
137 }
138 #[allow(dead_code)]
140 pub fn emit(self) -> String {
141 FSharpBackend::new().emit_function(&self.func)
142 }
143}
144#[derive(Debug, Clone)]
146#[allow(dead_code)]
147pub struct FSharpMutualGroup {
148 pub functions: Vec<FSharpFunction>,
150}
151impl FSharpMutualGroup {
152 #[allow(dead_code)]
154 pub fn emit(&self, backend: &FSharpBackend) -> String {
155 if self.functions.is_empty() {
156 return String::new();
157 }
158 let mut parts: Vec<String> = Vec::new();
159 for (i, func) in self.functions.iter().enumerate() {
160 let s = backend.emit_function(func);
161 if i == 0 {
162 parts.push(s.trim_end().to_string());
163 } else {
164 let replaced = if s.starts_with("let rec ") {
165 s.replacen("let rec ", "and rec ", 1)
166 } else if s.starts_with("let ") {
167 s.replacen("let ", "and ", 1)
168 } else {
169 s
170 };
171 parts.push(replaced.trim_end().to_string());
172 }
173 }
174 parts.join("\n") + "\n"
175 }
176}
177#[allow(dead_code)]
179pub struct FSharpStdLib;
180impl FSharpStdLib {
181 #[allow(dead_code)]
183 pub fn id_function() -> FSharpFunction {
184 FSharpFunctionBuilder::new("id")
185 .type_param("'a")
186 .param("x", Some(FSharpType::TypeVar("a".to_string())))
187 .returns(FSharpType::TypeVar("a".to_string()))
188 .body(fvar("x"))
189 .doc("Identity function.")
190 .build()
191 }
192 #[allow(dead_code)]
194 pub fn flip_function() -> FSharpFunction {
195 FSharpFunctionBuilder::new("flip")
196 .type_param("'a")
197 .type_param("'b")
198 .type_param("'c")
199 .param("f", None)
200 .param("x", None)
201 .param("y", None)
202 .body(fapp(fapp(fvar("f"), fvar("y")), fvar("x")))
203 .doc("Flip a two-argument function.")
204 .build()
205 }
206 #[allow(dead_code)]
208 pub fn const_function() -> FSharpFunction {
209 FSharpFunctionBuilder::new("konst")
210 .type_param("'a")
211 .type_param("'b")
212 .param("x", None)
213 .param("_y", None)
214 .body(fvar("x"))
215 .doc("Constant function (K combinator).")
216 .build()
217 }
218 #[allow(dead_code)]
220 pub fn foldl_function() -> FSharpFunction {
221 FSharpFunctionBuilder::new("foldl")
222 .recursive()
223 .type_param("'a")
224 .type_param("'b")
225 .param("f", None)
226 .param("acc", None)
227 .param("lst", None)
228 .body(FSharpExpr::Match(
229 Box::new(fvar("lst")),
230 vec![
231 (FSharpPattern::FsList(vec![]), fvar("acc")),
232 (
233 pcons(pvar("h"), pvar("t")),
234 fapp_multi(
235 fvar("foldl"),
236 vec![
237 fvar("f"),
238 fapp(fapp(fvar("f"), fvar("acc")), fvar("h")),
239 fvar("t"),
240 ],
241 ),
242 ),
243 ],
244 ))
245 .doc("Left fold over a list.")
246 .build()
247 }
248 #[allow(dead_code)]
250 pub fn filter_function() -> FSharpFunction {
251 FSharpFunctionBuilder::new("filterList")
252 .recursive()
253 .type_param("'a")
254 .param("pred", None)
255 .param("lst", None)
256 .body(FSharpExpr::Match(
257 Box::new(fvar("lst")),
258 vec![
259 (FSharpPattern::FsList(vec![]), FSharpExpr::FsList(vec![])),
260 (
261 pcons(pvar("h"), pvar("t")),
262 fif(
263 fapp(fvar("pred"), fvar("h")),
264 FSharpExpr::BinOp(
265 "::".to_string(),
266 Box::new(fvar("h")),
267 Box::new(fapp(fapp(fvar("filterList"), fvar("pred")), fvar("t"))),
268 ),
269 fapp(fapp(fvar("filterList"), fvar("pred")), fvar("t")),
270 ),
271 ),
272 ],
273 ))
274 .doc("Filter a list by a predicate.")
275 .build()
276 }
277 #[allow(dead_code)]
279 pub fn zip_with_function() -> FSharpFunction {
280 FSharpFunctionBuilder::new("zipWith")
281 .recursive()
282 .type_param("'a")
283 .type_param("'b")
284 .type_param("'c")
285 .param("f", None)
286 .param("xs", None)
287 .param("ys", None)
288 .body(FSharpExpr::Match(
289 Box::new(FSharpExpr::Tuple(vec![fvar("xs"), fvar("ys")])),
290 vec![
291 (
292 ptuple(vec![FSharpPattern::FsList(vec![]), FSharpPattern::Wildcard]),
293 FSharpExpr::FsList(vec![]),
294 ),
295 (
296 ptuple(vec![FSharpPattern::Wildcard, FSharpPattern::FsList(vec![])]),
297 FSharpExpr::FsList(vec![]),
298 ),
299 (
300 ptuple(vec![
301 pcons(pvar("x"), pvar("xt")),
302 pcons(pvar("y"), pvar("yt")),
303 ]),
304 FSharpExpr::BinOp(
305 "::".to_string(),
306 Box::new(fapp(fapp(fvar("f"), fvar("x")), fvar("y"))),
307 Box::new(fapp_multi(
308 fvar("zipWith"),
309 vec![fvar("f"), fvar("xt"), fvar("yt")],
310 )),
311 ),
312 ),
313 ],
314 ))
315 .doc("Zip two lists with a combining function.")
316 .build()
317 }
318}
319#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub enum FSharpType {
322 Int,
324 Int64,
326 Float,
328 Float32,
330 Bool,
332 FsString,
334 Char,
336 Unit,
338 Byte,
340 List(Box<FSharpType>),
342 Array(Box<FSharpType>),
344 Option(Box<FSharpType>),
346 Result(Box<FSharpType>, Box<FSharpType>),
348 Tuple(Vec<FSharpType>),
350 Fun(Box<FSharpType>, Box<FSharpType>),
352 Custom(String),
354 Generic(String, Vec<FSharpType>),
356 TypeVar(String),
358}
359#[derive(Debug, Clone)]
361pub struct FSharpFunction {
362 pub name: String,
364 pub is_recursive: bool,
366 pub is_inline: bool,
368 pub params: Vec<(String, Option<FSharpType>)>,
370 pub return_type: Option<FSharpType>,
372 pub body: FSharpExpr,
374 pub doc: Option<String>,
376 pub type_params: Vec<String>,
378}
379pub struct FSharpBackend {
381 pub(super) indent_str: String,
383}
384impl FSharpBackend {
385 pub fn new() -> Self {
387 FSharpBackend {
388 indent_str: " ".to_string(),
389 }
390 }
391 pub fn with_indent(indent: &str) -> Self {
393 FSharpBackend {
394 indent_str: indent.to_string(),
395 }
396 }
397 pub fn emit_type(&self, ty: &FSharpType) -> String {
399 format!("{}", ty)
400 }
401 pub fn emit_pattern(&self, pat: &FSharpPattern) -> String {
403 match pat {
404 FSharpPattern::Wildcard => "_".to_string(),
405 FSharpPattern::Var(v) => v.clone(),
406 FSharpPattern::Lit(l) => l.clone(),
407 FSharpPattern::Tuple(pats) => {
408 let inner = pats
409 .iter()
410 .map(|p| self.emit_pattern(p))
411 .collect::<Vec<_>>()
412 .join(", ");
413 format!("({})", inner)
414 }
415 FSharpPattern::Ctor(name, pats) => {
416 if pats.is_empty() {
417 name.clone()
418 } else {
419 let inner = pats
420 .iter()
421 .map(|p| self.emit_pattern(p))
422 .collect::<Vec<_>>()
423 .join(", ");
424 format!("{}({})", name, inner)
425 }
426 }
427 FSharpPattern::FsList(pats) => {
428 let inner = pats
429 .iter()
430 .map(|p| self.emit_pattern(p))
431 .collect::<Vec<_>>()
432 .join("; ");
433 format!("[{}]", inner)
434 }
435 FSharpPattern::Cons(head, tail) => {
436 format!("{} :: {}", self.emit_pattern(head), self.emit_pattern(tail))
437 }
438 FSharpPattern::As(inner, name) => {
439 format!("({} as {})", self.emit_pattern(inner), name)
440 }
441 FSharpPattern::When(inner, cond) => {
442 format!(
443 "{} when {}",
444 self.emit_pattern(inner),
445 self.emit_expr(cond, 0)
446 )
447 }
448 }
449 }
450 pub fn emit_expr(&self, expr: &FSharpExpr, depth: usize) -> String {
452 let pad = self.indent_str.repeat(depth);
453 let inner_pad = self.indent_str.repeat(depth + 1);
454 match expr {
455 FSharpExpr::Lit(s) => s.clone(),
456 FSharpExpr::Var(v) => v.clone(),
457 FSharpExpr::Raw(s) => s.clone(),
458 FSharpExpr::App(f, x) => {
459 format!(
460 "{} {}",
461 self.emit_expr_paren(f, depth),
462 self.emit_expr_paren(x, depth)
463 )
464 }
465 FSharpExpr::Lambda(param, body) => {
466 format!("fun {} -> {}", param, self.emit_expr(body, depth))
467 }
468 FSharpExpr::MultiLambda(params, body) => {
469 format!(
470 "fun {} -> {}",
471 params.join(" "),
472 self.emit_expr(body, depth)
473 )
474 }
475 FSharpExpr::Let(name, val, body) => {
476 format!(
477 "let {} = {}\n{}{}",
478 name,
479 self.emit_expr(val, depth),
480 pad,
481 self.emit_expr(body, depth)
482 )
483 }
484 FSharpExpr::LetRec(name, val, body) => {
485 format!(
486 "let rec {} = {}\n{}{}",
487 name,
488 self.emit_expr(val, depth),
489 pad,
490 self.emit_expr(body, depth)
491 )
492 }
493 FSharpExpr::Match(scrutinee, arms) => {
494 let mut out = format!("match {} with\n", self.emit_expr(scrutinee, depth));
495 for (pat, branch) in arms {
496 out += &format!(
497 "{}| {} -> {}\n",
498 inner_pad,
499 self.emit_pattern(pat),
500 self.emit_expr(branch, depth + 1)
501 );
502 }
503 out.trim_end().to_string()
504 }
505 FSharpExpr::If(cond, then_e, else_e) => {
506 format!(
507 "if {} then\n{}{}\n{}else\n{}{}",
508 self.emit_expr(cond, depth),
509 inner_pad,
510 self.emit_expr(then_e, depth + 1),
511 pad,
512 inner_pad,
513 self.emit_expr(else_e, depth + 1)
514 )
515 }
516 FSharpExpr::Tuple(elems) => {
517 let inner = elems
518 .iter()
519 .map(|e| self.emit_expr(e, depth))
520 .collect::<Vec<_>>()
521 .join(", ");
522 format!("({})", inner)
523 }
524 FSharpExpr::FsList(elems) => {
525 if elems.is_empty() {
526 "[]".to_string()
527 } else {
528 let inner = elems
529 .iter()
530 .map(|e| self.emit_expr(e, depth))
531 .collect::<Vec<_>>()
532 .join("; ");
533 format!("[{}]", inner)
534 }
535 }
536 FSharpExpr::FsArray(elems) => {
537 if elems.is_empty() {
538 "[||]".to_string()
539 } else {
540 let inner = elems
541 .iter()
542 .map(|e| self.emit_expr(e, depth))
543 .collect::<Vec<_>>()
544 .join("; ");
545 format!("[|{}|]", inner)
546 }
547 }
548 FSharpExpr::BinOp(op, lhs, rhs) => {
549 format!(
550 "{} {} {}",
551 self.emit_expr_paren(lhs, depth),
552 op,
553 self.emit_expr_paren(rhs, depth)
554 )
555 }
556 FSharpExpr::UnaryOp(op, operand) => {
557 format!("{}{}", op, self.emit_expr_paren(operand, depth))
558 }
559 FSharpExpr::Record(fields) => {
560 if fields.is_empty() {
561 "{}".to_string()
562 } else {
563 let inner = fields
564 .iter()
565 .map(|(k, v)| format!("{} = {}", k, self.emit_expr(v, depth)))
566 .collect::<Vec<_>>()
567 .join("; ");
568 format!("{{ {} }}", inner)
569 }
570 }
571 FSharpExpr::RecordUpdate(base, fields) => {
572 let inner = fields
573 .iter()
574 .map(|(k, v)| format!("{} = {}", k, self.emit_expr(v, depth)))
575 .collect::<Vec<_>>()
576 .join("; ");
577 format!("{{ {} with {} }}", self.emit_expr(base, depth), inner)
578 }
579 FSharpExpr::FieldAccess(base, field) => {
580 format!("{}.{}", self.emit_expr_paren(base, depth), field)
581 }
582 FSharpExpr::Seq(e1, e2) => {
583 format!(
584 "{}\n{}{}",
585 self.emit_expr(e1, depth),
586 pad,
587 self.emit_expr(e2, depth)
588 )
589 }
590 FSharpExpr::Ann(e, ty) => {
591 format!("({} : {})", self.emit_expr(e, depth), self.emit_type(ty))
592 }
593 FSharpExpr::Pipe(lhs, rhs) => {
594 format!(
595 "{}\n{}|> {}",
596 self.emit_expr(lhs, depth),
597 pad,
598 self.emit_expr(rhs, depth)
599 )
600 }
601 FSharpExpr::Ctor(name, args) => {
602 if args.is_empty() {
603 name.clone()
604 } else {
605 let arg_str = args
606 .iter()
607 .map(|a| self.emit_expr_paren(a, depth))
608 .collect::<Vec<_>>()
609 .join(" ");
610 format!("{} {}", name, arg_str)
611 }
612 }
613 FSharpExpr::Do(stmts) => {
614 let mut out = "do\n".to_string();
615 for s in stmts {
616 out += &format!("{}{}\n", inner_pad, self.emit_expr(s, depth + 1));
617 }
618 out.trim_end().to_string()
619 }
620 }
621 }
622 pub(super) fn emit_expr_paren(&self, expr: &FSharpExpr, depth: usize) -> String {
624 let needs_parens = matches!(
625 expr,
626 FSharpExpr::App(_, _)
627 | FSharpExpr::Lambda(_, _)
628 | FSharpExpr::MultiLambda(_, _)
629 | FSharpExpr::Let(_, _, _)
630 | FSharpExpr::LetRec(_, _, _)
631 | FSharpExpr::Match(_, _)
632 | FSharpExpr::If(_, _, _)
633 | FSharpExpr::BinOp(_, _, _)
634 | FSharpExpr::Seq(_, _)
635 | FSharpExpr::Pipe(_, _)
636 );
637 let s = self.emit_expr(expr, depth);
638 if needs_parens {
639 format!("({})", s)
640 } else {
641 s
642 }
643 }
644 pub fn emit_record(&self, rec: &FSharpRecord) -> String {
646 let mut out = String::new();
647 if let Some(doc) = &rec.doc {
648 out += &format!("/// {}\n", doc);
649 }
650 let params = if rec.type_params.is_empty() {
651 String::new()
652 } else {
653 format!("<{}>", rec.type_params.join(", "))
654 };
655 out += &format!("type {}{} =\n", rec.name, params);
656 out += " {";
657 let field_strs: Vec<String> = rec
658 .fields
659 .iter()
660 .map(|(name, ty)| format!(" {}: {}", name, self.emit_type(ty)))
661 .collect();
662 out += &field_strs.join(";");
663 out += " }\n";
664 out
665 }
666 pub fn emit_union(&self, union: &FSharpUnion) -> String {
668 let mut out = String::new();
669 if let Some(doc) = &union.doc {
670 out += &format!("/// {}\n", doc);
671 }
672 let params = if union.type_params.is_empty() {
673 String::new()
674 } else {
675 format!("<{}>", union.type_params.join(", "))
676 };
677 out += &format!("type {}{} =\n", union.name, params);
678 for case in &union.cases {
679 if case.fields.is_empty() {
680 out += &format!(" | {}\n", case.name);
681 } else {
682 let field_str: Vec<String> =
683 case.fields.iter().map(|t| self.emit_type(t)).collect();
684 out += &format!(" | {} of {}\n", case.name, field_str.join(" * "));
685 }
686 }
687 out
688 }
689 pub fn emit_function(&self, func: &FSharpFunction) -> String {
691 let mut out = String::new();
692 if let Some(doc) = &func.doc {
693 out += &format!("/// {}\n", doc);
694 }
695 let rec_kw = if func.is_recursive { " rec" } else { "" };
696 let inline_kw = if func.is_inline { " inline" } else { "" };
697 let type_params = if func.type_params.is_empty() {
698 String::new()
699 } else {
700 format!("<{}>", func.type_params.join(", "))
701 };
702 let params: Vec<String> = func
703 .params
704 .iter()
705 .map(|(name, ty)| match ty {
706 Some(t) => format!("({}: {})", name, self.emit_type(t)),
707 None => name.clone(),
708 })
709 .collect();
710 let ret_ann = func
711 .return_type
712 .as_ref()
713 .map(|t| format!(": {} ", self.emit_type(t)))
714 .unwrap_or_default();
715 out += &format!(
716 "let{}{}{} {}{} {}=\n {}\n",
717 rec_kw,
718 inline_kw,
719 type_params,
720 func.name,
721 if params.is_empty() {
722 String::new()
723 } else {
724 format!(" {}", params.join(" "))
725 },
726 ret_ann,
727 self.emit_expr(&func.body, 1)
728 );
729 out
730 }
731 pub fn emit_module(&self, module: &FSharpModule) -> String {
733 let mut out = String::new();
734 out += "// Generated by OxiLean F# Backend\n\n";
735 if module.auto_open {
736 out += "[<AutoOpen>]\n";
737 }
738 out += &format!("module {}\n\n", module.name);
739 for o in &module.opens {
740 out += &format!("open {}\n", o);
741 }
742 if !module.opens.is_empty() {
743 out.push('\n');
744 }
745 for rec in &module.records {
746 out += &self.emit_record(rec);
747 out.push('\n');
748 }
749 for union in &module.unions {
750 out += &self.emit_union(union);
751 out.push('\n');
752 }
753 for func in &module.functions {
754 out += &self.emit_function(func);
755 out.push('\n');
756 }
757 out
758 }
759}
760#[derive(Debug, Clone)]
762pub enum FSharpPattern {
763 Wildcard,
765 Var(String),
767 Lit(String),
769 Tuple(Vec<FSharpPattern>),
771 Ctor(String, Vec<FSharpPattern>),
773 FsList(Vec<FSharpPattern>),
775 Cons(Box<FSharpPattern>, Box<FSharpPattern>),
777 As(Box<FSharpPattern>, String),
779 When(Box<FSharpPattern>, Box<FSharpExpr>),
781}
782#[derive(Debug, Clone)]
784#[allow(dead_code)]
785pub struct FSharpMeasure {
786 pub name: String,
788 pub abbrev: Option<String>,
790}
791impl FSharpMeasure {
792 #[allow(dead_code)]
794 pub fn emit(&self) -> String {
795 match &self.abbrev {
796 None => format!("[<Measure>] type {}\n", self.name),
797 Some(abbrev) => format!("[<Measure>] type {} = {}\n", self.name, abbrev),
798 }
799 }
800}
801#[derive(Debug, Clone)]
803#[allow(dead_code)]
804pub struct FSharpActivePattern {
805 pub kind: ActivePatternKind,
807 pub params: Vec<String>,
809 pub body: FSharpExpr,
811}
812impl FSharpActivePattern {
813 #[allow(dead_code)]
815 pub fn emit(&self, backend: &FSharpBackend) -> String {
816 let name = match &self.kind {
817 ActivePatternKind::Total(cases) => format!("(|{}|)", cases.join("|")),
818 ActivePatternKind::Partial(case) => format!("(|{}|_|)", case),
819 ActivePatternKind::Parameterised(cases) => format!("(|{}|)", cases.join("|")),
820 };
821 let params = if self.params.is_empty() {
822 String::new()
823 } else {
824 format!(" {}", self.params.join(" "))
825 };
826 format!(
827 "let ({}){} =\n {}\n",
828 name,
829 params,
830 backend.emit_expr(&self.body, 1)
831 )
832 }
833}
834#[allow(dead_code)]
836pub struct FSharpModuleBuilder {
837 pub(super) module: FSharpModule,
838}
839impl FSharpModuleBuilder {
840 #[allow(dead_code)]
842 pub fn new(name: impl Into<String>) -> Self {
843 FSharpModuleBuilder {
844 module: FSharpModule {
845 name: name.into(),
846 auto_open: false,
847 records: vec![],
848 unions: vec![],
849 functions: vec![],
850 opens: vec![],
851 },
852 }
853 }
854 #[allow(dead_code)]
856 pub fn auto_open(mut self) -> Self {
857 self.module.auto_open = true;
858 self
859 }
860 #[allow(dead_code)]
862 pub fn open(mut self, ns: impl Into<String>) -> Self {
863 self.module.opens.push(ns.into());
864 self
865 }
866 #[allow(dead_code)]
868 pub fn record(mut self, rec: FSharpRecord) -> Self {
869 self.module.records.push(rec);
870 self
871 }
872 #[allow(dead_code)]
874 pub fn union(mut self, u: FSharpUnion) -> Self {
875 self.module.unions.push(u);
876 self
877 }
878 #[allow(dead_code)]
880 pub fn function(mut self, f: FSharpFunction) -> Self {
881 self.module.functions.push(f);
882 self
883 }
884 #[allow(dead_code)]
886 pub fn build(self) -> FSharpModule {
887 self.module
888 }
889 #[allow(dead_code)]
891 pub fn emit(self) -> String {
892 FSharpBackend::new().emit_module(&self.module)
893 }
894}
895#[derive(Debug, Default)]
897#[allow(dead_code)]
898pub struct FSharpModuleMetrics {
899 pub type_count: usize,
901 pub function_count: usize,
903 pub recursive_count: usize,
905 pub inline_count: usize,
907 pub estimated_lines: usize,
909}
910#[derive(Debug, Clone)]
912#[allow(dead_code)]
913pub struct FSharpUnionCaseNamed {
914 pub name: String,
916 pub named_fields: Vec<(String, FSharpType)>,
918}
919impl FSharpUnionCaseNamed {
920 #[allow(dead_code)]
922 pub fn emit(&self) -> String {
923 if self.named_fields.is_empty() {
924 return format!(" | {}\n", self.name);
925 }
926 let fields: Vec<String> = self
927 .named_fields
928 .iter()
929 .map(|(n, t)| format!("{}: {}", n, t))
930 .collect();
931 format!(" | {} of {{ {} }}\n", self.name, fields.join("; "))
932 }
933}
934#[derive(Debug, Clone, PartialEq)]
936#[allow(dead_code)]
937pub enum ActivePatternKind {
938 Total(Vec<String>),
940 Partial(String),
942 Parameterised(Vec<String>),
944}
945#[allow(dead_code)]
947pub struct FSharpNumericHelpers;
948impl FSharpNumericHelpers {
949 #[allow(dead_code)]
951 pub fn clamp() -> FSharpFunction {
952 FSharpFunction {
953 name: "clamp".to_string(),
954 is_recursive: false,
955 is_inline: true,
956 type_params: vec![],
957 params: vec![
958 ("lo".to_string(), Some(FSharpType::Int)),
959 ("hi".to_string(), Some(FSharpType::Int)),
960 ("x".to_string(), Some(FSharpType::Int)),
961 ],
962 return_type: Some(FSharpType::Int),
963 body: FSharpExpr::Raw("max lo (min hi x)".to_string()),
964 doc: Some("Clamp x to [lo, hi].".to_string()),
965 }
966 }
967 #[allow(dead_code)]
969 pub fn square() -> FSharpFunction {
970 FSharpFunction {
971 name: "square".to_string(),
972 is_recursive: false,
973 is_inline: true,
974 type_params: vec![],
975 params: vec![("x".to_string(), Some(FSharpType::Int))],
976 return_type: Some(FSharpType::Int),
977 body: fbinop("*", fvar("x"), fvar("x")),
978 doc: Some("Square of x.".to_string()),
979 }
980 }
981 #[allow(dead_code)]
983 pub fn pow_int() -> FSharpFunction {
984 FSharpFunction {
985 name: "powInt".to_string(),
986 is_recursive: true,
987 is_inline: false,
988 type_params: vec![],
989 params: vec![
990 ("b".to_string(), Some(FSharpType::Int)),
991 ("n".to_string(), Some(FSharpType::Int)),
992 ],
993 return_type: Some(FSharpType::Int),
994 body: FSharpExpr::Match(
995 Box::new(fvar("n")),
996 vec![
997 (plit("0"), fint(1)),
998 (
999 pvar("k"),
1000 fbinop(
1001 "*",
1002 fvar("b"),
1003 fapp_multi(
1004 fvar("powInt"),
1005 vec![fvar("b"), fbinop("-", fvar("k"), fint(1))],
1006 ),
1007 ),
1008 ),
1009 ],
1010 ),
1011 doc: Some("Integer power.".to_string()),
1012 }
1013 }
1014 #[allow(dead_code)]
1016 pub fn gcd() -> FSharpFunction {
1017 FSharpFunction {
1018 name: "gcd".to_string(),
1019 is_recursive: true,
1020 is_inline: false,
1021 type_params: vec![],
1022 params: vec![
1023 ("a".to_string(), Some(FSharpType::Int)),
1024 ("b".to_string(), Some(FSharpType::Int)),
1025 ],
1026 return_type: Some(FSharpType::Int),
1027 body: FSharpExpr::Match(
1028 Box::new(fvar("b")),
1029 vec![
1030 (plit("0"), fvar("a")),
1031 (
1032 pvar("k"),
1033 fapp_multi(
1034 fvar("gcd"),
1035 vec![fvar("k"), fbinop("%", fvar("a"), fvar("k"))],
1036 ),
1037 ),
1038 ],
1039 ),
1040 doc: Some("Greatest common divisor.".to_string()),
1041 }
1042 }
1043}
1044#[derive(Debug, Clone)]
1046pub struct FSharpRecord {
1047 pub name: String,
1049 pub type_params: Vec<String>,
1051 pub fields: Vec<(String, FSharpType)>,
1053 pub doc: Option<String>,
1055}
1056#[derive(Debug, Clone)]
1058#[allow(dead_code)]
1059pub struct FSharpTypeAlias {
1060 pub name: String,
1062 pub type_params: Vec<String>,
1064 pub aliased_type: FSharpType,
1066 pub doc: Option<String>,
1068}
1069impl FSharpTypeAlias {
1070 #[allow(dead_code)]
1072 pub fn emit(&self) -> String {
1073 let mut out = String::new();
1074 if let Some(doc) = &self.doc {
1075 out.push_str(&format!("/// {}\n", doc));
1076 }
1077 let params = if self.type_params.is_empty() {
1078 String::new()
1079 } else {
1080 format!("<{}>", self.type_params.join(", "))
1081 };
1082 out.push_str(&format!(
1083 "type {}{} = {}\n",
1084 self.name, params, self.aliased_type
1085 ));
1086 out
1087 }
1088}
1089#[allow(dead_code)]
1091pub struct FSharpSnippets;
1092impl FSharpSnippets {
1093 #[allow(dead_code)]
1095 pub fn option_map() -> String {
1096 "let optionMap f = function\n | Some x -> Some (f x)\n | None -> None\n".to_string()
1097 }
1098 #[allow(dead_code)]
1100 pub fn option_bind() -> String {
1101 "let optionBind f = function\n | Some x -> f x\n | None -> None\n".to_string()
1102 }
1103 #[allow(dead_code)]
1105 pub fn result_map() -> String {
1106 "let resultMap f = function\n | Ok x -> Ok (f x)\n | Error e -> Error e\n".to_string()
1107 }
1108 #[allow(dead_code)]
1110 pub fn list_fold() -> String {
1111 "let rec foldLeft f acc = function\n | [] -> acc\n | h :: t -> foldLeft f (f acc h) t\n"
1112 .to_string()
1113 }
1114 #[allow(dead_code)]
1116 pub fn identity() -> String {
1117 "let id x = x\n".to_string()
1118 }
1119 #[allow(dead_code)]
1121 pub fn constant() -> String {
1122 "let const x _ = x\n".to_string()
1123 }
1124 #[allow(dead_code)]
1126 pub fn compose() -> String {
1127 "let compose f g x = g (f x)\n".to_string()
1128 }
1129 #[allow(dead_code)]
1131 pub fn flip() -> String {
1132 "let flip f x y = f y x\n".to_string()
1133 }
1134 #[allow(dead_code)]
1136 pub fn curry() -> String {
1137 "let curry f a b = f (a, b)\n".to_string()
1138 }
1139 #[allow(dead_code)]
1141 pub fn uncurry() -> String {
1142 "let uncurry f (a, b) = f a b\n".to_string()
1143 }
1144 #[allow(dead_code)]
1146 pub fn memoize() -> String {
1147 "open System.Collections.Generic\n\
1148 let memoize f =\n \
1149 let cache = Dictionary<_,_>()\n \
1150 fun x ->\n \
1151 match cache.TryGetValue(x) with\n \
1152 | true, v -> v\n \
1153 | _ ->\n \
1154 let v = f x\n \
1155 cache.[x] <- v\n \
1156 v\n"
1157 .to_string()
1158 }
1159 #[allow(dead_code)]
1161 pub fn fix_point() -> String {
1162 "let fix f x = let rec go x = f go x in go x\n".to_string()
1163 }
1164}
1165#[derive(Debug, Clone)]
1167#[allow(dead_code)]
1168pub struct FSharpMember {
1169 pub name: String,
1171 pub params: Vec<(String, Option<FSharpType>)>,
1173 pub return_type: Option<FSharpType>,
1175 pub body: FSharpExpr,
1177 pub is_override: bool,
1179 pub is_static: bool,
1181 pub doc: Option<String>,
1183}
1184impl FSharpMember {
1185 #[allow(dead_code)]
1187 pub fn emit(&self) -> String {
1188 let mut out = String::new();
1189 if let Some(doc) = &self.doc {
1190 out.push_str(&format!(" /// {}\n", doc));
1191 }
1192 let kw = if self.is_override {
1193 "override"
1194 } else {
1195 "member"
1196 };
1197 let stat = if self.is_static { " static" } else { "" };
1198 let params: Vec<String> = self
1199 .params
1200 .iter()
1201 .map(|(n, t)| match t {
1202 Some(ty) => format!("({}: {})", n, ty),
1203 None => n.clone(),
1204 })
1205 .collect();
1206 let ret = self
1207 .return_type
1208 .as_ref()
1209 .map(|t| format!(": {} ", t))
1210 .unwrap_or_default();
1211 out.push_str(&format!(
1212 " {}{} this.{} {} {}=\n {}\n",
1213 kw,
1214 stat,
1215 self.name,
1216 params.join(" "),
1217 ret,
1218 "..."
1219 ));
1220 out
1221 }
1222}
1223#[derive(Debug, Clone, PartialEq)]
1225#[allow(dead_code)]
1226pub enum ComputationExprKind {
1227 Async,
1229 Seq,
1231 Result,
1233 OptionCe,
1235 Custom(String),
1237}
1238#[derive(Debug, Clone)]
1240#[allow(dead_code)]
1241pub struct FSharpInterface {
1242 pub name: String,
1244 pub type_params: Vec<String>,
1246 pub methods: Vec<(String, Vec<(String, FSharpType)>, FSharpType)>,
1248 pub properties: Vec<(String, FSharpType, bool)>,
1250 pub doc: Option<String>,
1252}
1253impl FSharpInterface {
1254 #[allow(dead_code)]
1256 pub fn emit(&self) -> String {
1257 let mut out = String::new();
1258 if let Some(doc) = &self.doc {
1259 out.push_str(&format!("/// {}\n", doc));
1260 }
1261 let params = if self.type_params.is_empty() {
1262 String::new()
1263 } else {
1264 format!("<{}>", self.type_params.join(", "))
1265 };
1266 out.push_str(&format!("type {}{} =\n", self.name, params));
1267 for (name, params, ret) in &self.methods {
1268 let param_str: Vec<String> = params
1269 .iter()
1270 .map(|(n, t)| format!("{}: {}", n, t))
1271 .collect();
1272 out.push_str(&format!(
1273 " abstract member {}: {} -> {}\n",
1274 name,
1275 param_str.join(" -> "),
1276 ret
1277 ));
1278 }
1279 for (name, ty, is_settable) in &self.properties {
1280 let access = if *is_settable { "get, set" } else { "get" };
1281 out.push_str(&format!(
1282 " abstract member {}: {} with {}\n",
1283 name, ty, access
1284 ));
1285 }
1286 out
1287 }
1288}
1289#[derive(Debug, Clone)]
1291pub struct FSharpUnionCase {
1292 pub name: String,
1294 pub fields: Vec<FSharpType>,
1296}
1297#[derive(Debug, Clone)]
1299pub struct FSharpModule {
1300 pub name: String,
1302 pub auto_open: bool,
1304 pub records: Vec<FSharpRecord>,
1306 pub unions: Vec<FSharpUnion>,
1308 pub functions: Vec<FSharpFunction>,
1310 pub opens: Vec<String>,
1312}
1313#[derive(Debug, Clone)]
1315#[allow(dead_code)]
1316pub struct FSharpAttribute {
1317 pub name: String,
1319 pub args: Vec<String>,
1321 pub named_args: Vec<(String, String)>,
1323}
1324impl FSharpAttribute {
1325 #[allow(dead_code)]
1327 pub fn simple(name: impl Into<String>) -> Self {
1328 FSharpAttribute {
1329 name: name.into(),
1330 args: vec![],
1331 named_args: vec![],
1332 }
1333 }
1334 #[allow(dead_code)]
1336 pub fn with_args(name: impl Into<String>, args: Vec<String>) -> Self {
1337 FSharpAttribute {
1338 name: name.into(),
1339 args,
1340 named_args: vec![],
1341 }
1342 }
1343 #[allow(dead_code)]
1345 pub fn emit(&self) -> String {
1346 if self.args.is_empty() && self.named_args.is_empty() {
1347 return format!("[<{}>]", self.name);
1348 }
1349 let mut parts: Vec<String> = self.args.clone();
1350 for (k, v) in &self.named_args {
1351 parts.push(format!("{} = {}", k, v));
1352 }
1353 format!("[<{}({})>]", self.name, parts.join(", "))
1354 }
1355}
1356#[derive(Debug, Clone)]
1358#[allow(dead_code)]
1359pub struct FSharpClass {
1360 pub name: String,
1362 pub type_params: Vec<String>,
1364 pub ctor_params: Vec<(String, FSharpType)>,
1366 pub members: Vec<FSharpMember>,
1368 pub implements: Vec<String>,
1370 pub inherits: Option<String>,
1372 pub doc: Option<String>,
1374 pub attributes: Vec<FSharpAttribute>,
1376}
1377impl FSharpClass {
1378 #[allow(dead_code)]
1380 pub fn emit(&self) -> String {
1381 let mut out = String::new();
1382 for attr in &self.attributes {
1383 out.push_str(&format!("{}\n", attr.emit()));
1384 }
1385 if let Some(doc) = &self.doc {
1386 out.push_str(&format!("/// {}\n", doc));
1387 }
1388 let params = if self.type_params.is_empty() {
1389 String::new()
1390 } else {
1391 format!("<{}>", self.type_params.join(", "))
1392 };
1393 let ctor_str: Vec<String> = self
1394 .ctor_params
1395 .iter()
1396 .map(|(n, t)| format!("{}: {}", n, t))
1397 .collect();
1398 out.push_str(&format!(
1399 "type {}{}({}) =\n",
1400 self.name,
1401 params,
1402 ctor_str.join(", ")
1403 ));
1404 if let Some(base) = &self.inherits {
1405 out.push_str(&format!(" inherit {}\n", base));
1406 }
1407 for iface in &self.implements {
1408 out.push_str(&format!(" interface {}\n", iface));
1409 }
1410 for member in &self.members {
1411 out.push_str(&member.emit());
1412 }
1413 out
1414 }
1415}