Skip to main content

oxilean_codegen/fsharp_backend/
types.rs

1//! Auto-generated module
2//!
3//! πŸ€– Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6
7/// An F# expression node.
8#[derive(Debug, Clone)]
9pub enum FSharpExpr {
10    /// Literal source (integer, float, string, bool, unit `()`)
11    Lit(String),
12    /// Variable / identifier reference
13    Var(String),
14    /// Function application: `f x`
15    App(Box<FSharpExpr>, Box<FSharpExpr>),
16    /// Lambda: `fun param -> body`
17    Lambda(String, Box<FSharpExpr>),
18    /// Multi-parameter lambda: `fun a b c -> body`
19    MultiLambda(Vec<String>, Box<FSharpExpr>),
20    /// Let binding: `let name = value in body`
21    Let(String, Box<FSharpExpr>, Box<FSharpExpr>),
22    /// Recursive let binding: `let rec name = value in body`
23    LetRec(String, Box<FSharpExpr>, Box<FSharpExpr>),
24    /// Pattern match: `match expr with | pat -> branch`
25    Match(Box<FSharpExpr>, Vec<(FSharpPattern, FSharpExpr)>),
26    /// If-then-else
27    If(Box<FSharpExpr>, Box<FSharpExpr>, Box<FSharpExpr>),
28    /// Tuple construction: `(a, b, c)`
29    Tuple(Vec<FSharpExpr>),
30    /// List literal: `[a; b; c]`
31    FsList(Vec<FSharpExpr>),
32    /// Array literal: `[| a; b; c |]`
33    FsArray(Vec<FSharpExpr>),
34    /// Binary operator: `lhs op rhs`
35    BinOp(String, Box<FSharpExpr>, Box<FSharpExpr>),
36    /// Unary operator: `op expr`
37    UnaryOp(String, Box<FSharpExpr>),
38    /// Record construction: `{ field1 = v1; field2 = v2 }`
39    Record(Vec<(String, FSharpExpr)>),
40    /// Record update: `{ base with field = v }`
41    RecordUpdate(Box<FSharpExpr>, Vec<(String, FSharpExpr)>),
42    /// Field access: `expr.Field`
43    FieldAccess(Box<FSharpExpr>, String),
44    /// Sequence: `expr1; expr2` (semicolon-separated, result is `expr2`)
45    Seq(Box<FSharpExpr>, Box<FSharpExpr>),
46    /// Type annotation: `(expr : T)`
47    Ann(Box<FSharpExpr>, FSharpType),
48    /// Pipe forward: `expr |> f`
49    Pipe(Box<FSharpExpr>, Box<FSharpExpr>),
50    /// Constructor application: `Some x`, `MyCase(a, b)`
51    Ctor(String, Vec<FSharpExpr>),
52    /// `do { ... }` computation expression (simplified)
53    Do(Vec<FSharpExpr>),
54    /// Raw source snippet (escape hatch)
55    Raw(String),
56}
57/// A discriminated union declaration: `type T = | A | B of int`.
58#[derive(Debug, Clone)]
59pub struct FSharpUnion {
60    /// Type name
61    pub name: String,
62    /// Generic type parameters
63    pub type_params: Vec<String>,
64    /// Constructor cases
65    pub cases: Vec<FSharpUnionCase>,
66    /// Optional XML doc comment
67    pub doc: Option<String>,
68}
69/// Fluent builder for `FSharpFunction`.
70#[allow(dead_code)]
71pub struct FSharpFunctionBuilder {
72    pub(super) func: FSharpFunction,
73}
74impl FSharpFunctionBuilder {
75    /// Start a function with the given name.
76    #[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    /// Mark as recursive.
92    #[allow(dead_code)]
93    pub fn recursive(mut self) -> Self {
94        self.func.is_recursive = true;
95        self
96    }
97    /// Mark as inline.
98    #[allow(dead_code)]
99    pub fn inline(mut self) -> Self {
100        self.func.is_inline = true;
101        self
102    }
103    /// Add a type parameter.
104    #[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    /// Add a parameter (with optional type annotation).
110    #[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    /// Set return type.
116    #[allow(dead_code)]
117    pub fn returns(mut self, ty: FSharpType) -> Self {
118        self.func.return_type = Some(ty);
119        self
120    }
121    /// Set the body.
122    #[allow(dead_code)]
123    pub fn body(mut self, expr: FSharpExpr) -> Self {
124        self.func.body = expr;
125        self
126    }
127    /// Add a doc comment.
128    #[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    /// Finalise.
134    #[allow(dead_code)]
135    pub fn build(self) -> FSharpFunction {
136        self.func
137    }
138    /// Emit the function directly.
139    #[allow(dead_code)]
140    pub fn emit(self) -> String {
141        FSharpBackend::new().emit_function(&self.func)
142    }
143}
144/// A group of mutually recursive `let rec ... and ...` functions.
145#[derive(Debug, Clone)]
146#[allow(dead_code)]
147pub struct FSharpMutualGroup {
148    /// Functions in this mutual group (all sharing `let rec ... and ...`).
149    pub functions: Vec<FSharpFunction>,
150}
151impl FSharpMutualGroup {
152    /// Emit the mutual group.
153    #[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/// Common standard library–style helper definitions.
178#[allow(dead_code)]
179pub struct FSharpStdLib;
180impl FSharpStdLib {
181    /// Build the `id` function.
182    #[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    /// Build the `flip` function.
193    #[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    /// Build the `const` function.
207    #[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    /// Build a `foldl` function.
219    #[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    /// Build a `filter` function.
249    #[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    /// Build a `zipWith` function.
278    #[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/// An F# type expression used during code generation.
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub enum FSharpType {
322    /// `int` β€” default integer (32-bit on .NET)
323    Int,
324    /// `int64` β€” 64-bit signed integer
325    Int64,
326    /// `float` β€” 64-bit IEEE floating-point (`double` in .NET)
327    Float,
328    /// `float32` β€” 32-bit IEEE floating-point
329    Float32,
330    /// `bool` β€” boolean
331    Bool,
332    /// `string` β€” .NET `System.String`
333    FsString,
334    /// `char` β€” .NET `System.Char` (UTF-16 code unit)
335    Char,
336    /// `unit` β€” `()`
337    Unit,
338    /// `byte` β€” unsigned 8-bit integer
339    Byte,
340    /// `T list` β€” immutable singly-linked list
341    List(Box<FSharpType>),
342    /// `T array` β€” mutable .NET array
343    Array(Box<FSharpType>),
344    /// `T option` β€” optional value
345    Option(Box<FSharpType>),
346    /// `Result<T, E>` β€” result type
347    Result(Box<FSharpType>, Box<FSharpType>),
348    /// `T * U * …` β€” tuple type
349    Tuple(Vec<FSharpType>),
350    /// `T -> U` β€” function type
351    Fun(Box<FSharpType>, Box<FSharpType>),
352    /// Named type (discriminated union, record, alias)
353    Custom(String),
354    /// Generic type application: `Map<K,V>`, `IEnumerable<T>`, …
355    Generic(String, Vec<FSharpType>),
356    /// Polymorphic type variable: `'a`, `'b`
357    TypeVar(String),
358}
359/// A top-level F# function or value definition.
360#[derive(Debug, Clone)]
361pub struct FSharpFunction {
362    /// Function name (lowercase by convention)
363    pub name: String,
364    /// Whether this is a `let rec` definition
365    pub is_recursive: bool,
366    /// Whether this is a `let inline` definition
367    pub is_inline: bool,
368    /// Parameters: `(name, optional_type_annotation)`
369    pub params: Vec<(String, Option<FSharpType>)>,
370    /// Return type annotation (optional)
371    pub return_type: Option<FSharpType>,
372    /// Function body expression
373    pub body: FSharpExpr,
374    /// Optional XML doc comment
375    pub doc: Option<String>,
376    /// Generic type parameters (e.g. `["'a", "'b"]`)
377    pub type_params: Vec<String>,
378}
379/// F# code generation backend.
380pub struct FSharpBackend {
381    /// Indentation string (default: four spaces)
382    pub(super) indent_str: String,
383}
384impl FSharpBackend {
385    /// Create a new `FSharpBackend` with default settings.
386    pub fn new() -> Self {
387        FSharpBackend {
388            indent_str: "    ".to_string(),
389        }
390    }
391    /// Create a backend with a custom indentation string.
392    pub fn with_indent(indent: &str) -> Self {
393        FSharpBackend {
394            indent_str: indent.to_string(),
395        }
396    }
397    /// Emit an [`FSharpType`] as F# source text.
398    pub fn emit_type(&self, ty: &FSharpType) -> String {
399        format!("{}", ty)
400    }
401    /// Emit an [`FSharpPattern`] as F# source text.
402    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    /// Emit an [`FSharpExpr`] at a given indentation depth.
451    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    /// Emit an expression, wrapping in parentheses if needed for disambiguation.
623    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    /// Emit a record type declaration.
645    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    /// Emit a discriminated union type declaration.
667    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    /// Emit an [`FSharpFunction`] as F# source text.
690    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    /// Emit an [`FSharpModule`] as a complete F# source file.
732    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/// An F# pattern used in `match` arms.
761#[derive(Debug, Clone)]
762pub enum FSharpPattern {
763    /// Wildcard `_`
764    Wildcard,
765    /// Variable binding `name`
766    Var(String),
767    /// Literal pattern
768    Lit(String),
769    /// Tuple pattern `(a, b)`
770    Tuple(Vec<FSharpPattern>),
771    /// Constructor pattern `Some x` / `MyCase(a, b)`
772    Ctor(String, Vec<FSharpPattern>),
773    /// List pattern `[a; b]`
774    FsList(Vec<FSharpPattern>),
775    /// Cons pattern `h :: t`
776    Cons(Box<FSharpPattern>, Box<FSharpPattern>),
777    /// As-pattern `p as name`
778    As(Box<FSharpPattern>, String),
779    /// Guard: when `condition`
780    When(Box<FSharpPattern>, Box<FSharpExpr>),
781}
782/// A unit of measure declaration.
783#[derive(Debug, Clone)]
784#[allow(dead_code)]
785pub struct FSharpMeasure {
786    /// Measure name (e.g. `kg`, `m`, `s`).
787    pub name: String,
788    /// Optional abbreviation.
789    pub abbrev: Option<String>,
790}
791impl FSharpMeasure {
792    /// Emit the measure declaration.
793    #[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/// An active pattern definition.
802#[derive(Debug, Clone)]
803#[allow(dead_code)]
804pub struct FSharpActivePattern {
805    /// The active pattern kind.
806    pub kind: ActivePatternKind,
807    /// Parameters.
808    pub params: Vec<String>,
809    /// Body.
810    pub body: FSharpExpr,
811}
812impl FSharpActivePattern {
813    /// Emit the active pattern.
814    #[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/// Fluent builder for constructing `FSharpModule` objects.
835#[allow(dead_code)]
836pub struct FSharpModuleBuilder {
837    pub(super) module: FSharpModule,
838}
839impl FSharpModuleBuilder {
840    /// Start building a module.
841    #[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    /// Mark the module as `[<AutoOpen>]`.
855    #[allow(dead_code)]
856    pub fn auto_open(mut self) -> Self {
857        self.module.auto_open = true;
858        self
859    }
860    /// Add an `open` directive.
861    #[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    /// Add a record type.
867    #[allow(dead_code)]
868    pub fn record(mut self, rec: FSharpRecord) -> Self {
869        self.module.records.push(rec);
870        self
871    }
872    /// Add a discriminated union.
873    #[allow(dead_code)]
874    pub fn union(mut self, u: FSharpUnion) -> Self {
875        self.module.unions.push(u);
876        self
877    }
878    /// Add a function.
879    #[allow(dead_code)]
880    pub fn function(mut self, f: FSharpFunction) -> Self {
881        self.module.functions.push(f);
882        self
883    }
884    /// Finalise and return the module.
885    #[allow(dead_code)]
886    pub fn build(self) -> FSharpModule {
887        self.module
888    }
889    /// Emit the module directly.
890    #[allow(dead_code)]
891    pub fn emit(self) -> String {
892        FSharpBackend::new().emit_module(&self.module)
893    }
894}
895/// Simple metrics gathered from an `FSharpModule`.
896#[derive(Debug, Default)]
897#[allow(dead_code)]
898pub struct FSharpModuleMetrics {
899    /// Number of type declarations.
900    pub type_count: usize,
901    /// Number of function declarations.
902    pub function_count: usize,
903    /// Number of recursive functions.
904    pub recursive_count: usize,
905    /// Number of inline functions.
906    pub inline_count: usize,
907    /// Estimated total source lines.
908    pub estimated_lines: usize,
909}
910/// A discriminated union case with named fields (record-like).
911#[derive(Debug, Clone)]
912#[allow(dead_code)]
913pub struct FSharpUnionCaseNamed {
914    /// Case name.
915    pub name: String,
916    /// Named fields: `(field_name, type)`.
917    pub named_fields: Vec<(String, FSharpType)>,
918}
919impl FSharpUnionCaseNamed {
920    /// Emit as `| Name of { field1: T1; field2: T2 }`.
921    #[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/// Kind of F# active pattern.
935#[derive(Debug, Clone, PartialEq)]
936#[allow(dead_code)]
937pub enum ActivePatternKind {
938    /// Total active pattern `(|A|B|)`.
939    Total(Vec<String>),
940    /// Partial active pattern `(|A|_|)`.
941    Partial(String),
942    /// Parameterised active pattern `(|A|B|) arg`.
943    Parameterised(Vec<String>),
944}
945/// Build common numeric helper functions.
946#[allow(dead_code)]
947pub struct FSharpNumericHelpers;
948impl FSharpNumericHelpers {
949    /// `clamp min max value` function.
950    #[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    /// `square x = x * x` function.
968    #[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    /// `pow base exp` (integer exponentiation) function.
982    #[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    /// `gcd` function.
1015    #[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/// A record type declaration: `type Name = { field1: T1; field2: T2 }`.
1045#[derive(Debug, Clone)]
1046pub struct FSharpRecord {
1047    /// Type name
1048    pub name: String,
1049    /// Generic type parameters (e.g. `["'a", "'b"]`)
1050    pub type_params: Vec<String>,
1051    /// Fields: `(field_name, type)`
1052    pub fields: Vec<(String, FSharpType)>,
1053    /// Optional XML doc comment
1054    pub doc: Option<String>,
1055}
1056/// A type alias: `type Alias = ExistingType`.
1057#[derive(Debug, Clone)]
1058#[allow(dead_code)]
1059pub struct FSharpTypeAlias {
1060    /// Alias name.
1061    pub name: String,
1062    /// Generic type parameters.
1063    pub type_params: Vec<String>,
1064    /// The aliased type.
1065    pub aliased_type: FSharpType,
1066    /// Optional doc comment.
1067    pub doc: Option<String>,
1068}
1069impl FSharpTypeAlias {
1070    /// Emit the type alias.
1071    #[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/// Pre-built F# code snippets for common patterns.
1090#[allow(dead_code)]
1091pub struct FSharpSnippets;
1092impl FSharpSnippets {
1093    /// An option map function.
1094    #[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    /// An option bind function.
1099    #[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    /// A result map function.
1104    #[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    /// A list fold-left function.
1109    #[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    /// An identity function.
1115    #[allow(dead_code)]
1116    pub fn identity() -> String {
1117        "let id x = x\n".to_string()
1118    }
1119    /// A constant function.
1120    #[allow(dead_code)]
1121    pub fn constant() -> String {
1122        "let const x _ = x\n".to_string()
1123    }
1124    /// Function composition `>>`.
1125    #[allow(dead_code)]
1126    pub fn compose() -> String {
1127        "let compose f g x = g (f x)\n".to_string()
1128    }
1129    /// Flip a two-argument function.
1130    #[allow(dead_code)]
1131    pub fn flip() -> String {
1132        "let flip f x y = f y x\n".to_string()
1133    }
1134    /// Curry a function taking a tuple.
1135    #[allow(dead_code)]
1136    pub fn curry() -> String {
1137        "let curry f a b = f (a, b)\n".to_string()
1138    }
1139    /// Uncurry a curried function.
1140    #[allow(dead_code)]
1141    pub fn uncurry() -> String {
1142        "let uncurry f (a, b) = f a b\n".to_string()
1143    }
1144    /// A memoization function using a dictionary.
1145    #[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    /// A fixed-point (Y combinator) function.
1160    #[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/// A member in an F# class or object expression.
1166#[derive(Debug, Clone)]
1167#[allow(dead_code)]
1168pub struct FSharpMember {
1169    /// Member name.
1170    pub name: String,
1171    /// Parameters.
1172    pub params: Vec<(String, Option<FSharpType>)>,
1173    /// Return type annotation.
1174    pub return_type: Option<FSharpType>,
1175    /// Body expression.
1176    pub body: FSharpExpr,
1177    /// Whether this is an override.
1178    pub is_override: bool,
1179    /// Whether this is static.
1180    pub is_static: bool,
1181    /// Optional doc comment.
1182    pub doc: Option<String>,
1183}
1184impl FSharpMember {
1185    /// Emit the member.
1186    #[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/// A computation expression kind.
1224#[derive(Debug, Clone, PartialEq)]
1225#[allow(dead_code)]
1226pub enum ComputationExprKind {
1227    /// `async { ... }` β€” asynchronous computation.
1228    Async,
1229    /// `seq { ... }` β€” lazy sequence.
1230    Seq,
1231    /// `result { ... }` β€” result monad.
1232    Result,
1233    /// `option { ... }` β€” option monad.
1234    OptionCe,
1235    /// Custom builder: `builder { ... }`.
1236    Custom(String),
1237}
1238/// A .NET/F# interface declaration.
1239#[derive(Debug, Clone)]
1240#[allow(dead_code)]
1241pub struct FSharpInterface {
1242    /// Interface name (conventionally starts with `I`).
1243    pub name: String,
1244    /// Generic type parameters.
1245    pub type_params: Vec<String>,
1246    /// Abstract method signatures: `(name, params, return_type)`.
1247    pub methods: Vec<(String, Vec<(String, FSharpType)>, FSharpType)>,
1248    /// Abstract property signatures: `(name, type)`.
1249    pub properties: Vec<(String, FSharpType, bool)>,
1250    /// Optional XML doc comment.
1251    pub doc: Option<String>,
1252}
1253impl FSharpInterface {
1254    /// Emit the interface declaration.
1255    #[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/// A discriminated union case: `| Case of T1 * T2`.
1290#[derive(Debug, Clone)]
1291pub struct FSharpUnionCase {
1292    /// Constructor name (must start with uppercase)
1293    pub name: String,
1294    /// Payload types (empty for constant constructors)
1295    pub fields: Vec<FSharpType>,
1296}
1297/// An F# module definition.
1298#[derive(Debug, Clone)]
1299pub struct FSharpModule {
1300    /// Fully-qualified module name, e.g. `"OxiLean.Math"`
1301    pub name: String,
1302    /// Whether this is an `open` module (auto-opened)
1303    pub auto_open: bool,
1304    /// Record type declarations
1305    pub records: Vec<FSharpRecord>,
1306    /// Discriminated union declarations
1307    pub unions: Vec<FSharpUnion>,
1308    /// Function / value definitions
1309    pub functions: Vec<FSharpFunction>,
1310    /// `open` directives
1311    pub opens: Vec<String>,
1312}
1313/// A .NET attribute applied to a type, function, or property.
1314#[derive(Debug, Clone)]
1315#[allow(dead_code)]
1316pub struct FSharpAttribute {
1317    /// Attribute name (e.g. `Serializable`, `DllImport`).
1318    pub name: String,
1319    /// Positional arguments as raw strings.
1320    pub args: Vec<String>,
1321    /// Named arguments as `(key, value)` pairs.
1322    pub named_args: Vec<(String, String)>,
1323}
1324impl FSharpAttribute {
1325    /// Build a simple attribute with no arguments.
1326    #[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    /// Build an attribute with positional arguments.
1335    #[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    /// Emit the attribute as `[<Name(args)>]`.
1344    #[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/// An F# class declaration.
1357#[derive(Debug, Clone)]
1358#[allow(dead_code)]
1359pub struct FSharpClass {
1360    /// Class name.
1361    pub name: String,
1362    /// Generic type parameters.
1363    pub type_params: Vec<String>,
1364    /// Constructor parameters.
1365    pub ctor_params: Vec<(String, FSharpType)>,
1366    /// Member methods.
1367    pub members: Vec<FSharpMember>,
1368    /// Interfaces this class implements.
1369    pub implements: Vec<String>,
1370    /// Optional base class.
1371    pub inherits: Option<String>,
1372    /// Optional doc comment.
1373    pub doc: Option<String>,
1374    /// Attributes on the class.
1375    pub attributes: Vec<FSharpAttribute>,
1376}
1377impl FSharpClass {
1378    /// Emit the class declaration.
1379    #[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}