1use super::types::{
6 ActivePatternKind, ComputationExprKind, FSharpActivePattern, FSharpAttribute, FSharpBackend,
7 FSharpExpr, FSharpFunction, FSharpFunctionBuilder, FSharpInterface, FSharpMeasure,
8 FSharpModule, FSharpModuleBuilder, FSharpModuleMetrics, FSharpMutualGroup,
9 FSharpNumericHelpers, FSharpPattern, FSharpRecord, FSharpSnippets, FSharpStdLib, FSharpType,
10 FSharpTypeAlias, FSharpUnion, FSharpUnionCase, FSharpUnionCaseNamed,
11};
12
13pub(super) fn paren_type(ty: &FSharpType) -> String {
15 match ty {
16 FSharpType::Fun(_, _) | FSharpType::Tuple(_) => format!("({})", ty),
17 _ => format!("{}", ty),
18 }
19}
20pub(super) fn paren_fun_type(ty: &FSharpType) -> String {
22 match ty {
23 FSharpType::Fun(_, _) => format!("({})", ty),
24 _ => format!("{}", ty),
25 }
26}
27#[cfg(test)]
28mod tests {
29 use super::*;
30 pub(super) fn b() -> FSharpBackend {
31 FSharpBackend::new()
32 }
33 #[test]
34 pub(super) fn test_type_int() {
35 assert_eq!(b().emit_type(&FSharpType::Int), "int");
36 }
37 #[test]
38 pub(super) fn test_type_fun() {
39 let ty = FSharpType::Fun(Box::new(FSharpType::Int), Box::new(FSharpType::Bool));
40 assert_eq!(b().emit_type(&ty), "int -> bool");
41 }
42 #[test]
43 pub(super) fn test_type_list() {
44 let ty = FSharpType::List(Box::new(FSharpType::FsString));
45 assert_eq!(b().emit_type(&ty), "string list");
46 }
47 #[test]
48 pub(super) fn test_type_option() {
49 let ty = FSharpType::Option(Box::new(FSharpType::Int));
50 assert_eq!(b().emit_type(&ty), "int option");
51 }
52 #[test]
53 pub(super) fn test_type_tuple() {
54 let ty = FSharpType::Tuple(vec![FSharpType::Int, FSharpType::Bool]);
55 assert_eq!(b().emit_type(&ty), "int * bool");
56 }
57 #[test]
58 pub(super) fn test_type_generic() {
59 let ty = FSharpType::Generic(
60 "Map".to_string(),
61 vec![FSharpType::FsString, FSharpType::Int],
62 );
63 assert_eq!(b().emit_type(&ty), "Map<string, int>");
64 }
65 #[test]
66 pub(super) fn test_expr_lit() {
67 assert_eq!(b().emit_expr(&FSharpExpr::Lit("42".to_string()), 0), "42");
68 }
69 #[test]
70 pub(super) fn test_expr_var() {
71 assert_eq!(b().emit_expr(&FSharpExpr::Var("x".to_string()), 0), "x");
72 }
73 #[test]
74 pub(super) fn test_expr_lambda() {
75 let e = FSharpExpr::Lambda("x".to_string(), Box::new(FSharpExpr::Var("x".to_string())));
76 assert_eq!(b().emit_expr(&e, 0), "fun x -> x");
77 }
78 #[test]
79 pub(super) fn test_expr_binop() {
80 let e = FSharpExpr::BinOp(
81 "+".to_string(),
82 Box::new(FSharpExpr::Lit("1".to_string())),
83 Box::new(FSharpExpr::Lit("2".to_string())),
84 );
85 assert_eq!(b().emit_expr(&e, 0), "1 + 2");
86 }
87 #[test]
88 pub(super) fn test_expr_list() {
89 let e = FSharpExpr::FsList(vec![
90 FSharpExpr::Lit("1".to_string()),
91 FSharpExpr::Lit("2".to_string()),
92 ]);
93 assert_eq!(b().emit_expr(&e, 0), "[1; 2]");
94 }
95 #[test]
96 pub(super) fn test_expr_empty_list() {
97 assert_eq!(b().emit_expr(&FSharpExpr::FsList(vec![]), 0), "[]");
98 }
99 #[test]
100 pub(super) fn test_expr_array() {
101 let e = FSharpExpr::FsArray(vec![FSharpExpr::Lit("3".to_string())]);
102 assert_eq!(b().emit_expr(&e, 0), "[|3|]");
103 }
104 #[test]
105 pub(super) fn test_expr_tuple() {
106 let e = FSharpExpr::Tuple(vec![
107 FSharpExpr::Lit("1".to_string()),
108 FSharpExpr::Lit("2".to_string()),
109 ]);
110 assert_eq!(b().emit_expr(&e, 0), "(1, 2)");
111 }
112 #[test]
113 pub(super) fn test_expr_record() {
114 let e = FSharpExpr::Record(vec![
115 ("x".to_string(), FSharpExpr::Lit("0".to_string())),
116 ("y".to_string(), FSharpExpr::Lit("1".to_string())),
117 ]);
118 let s = b().emit_expr(&e, 0);
119 assert!(s.contains("x = 0"), "got: {}", s);
120 assert!(s.contains("y = 1"), "got: {}", s);
121 }
122 #[test]
123 pub(super) fn test_expr_pipe() {
124 let e = FSharpExpr::Pipe(
125 Box::new(FSharpExpr::Var("xs".to_string())),
126 Box::new(FSharpExpr::Var("List.sort".to_string())),
127 );
128 let s = b().emit_expr(&e, 0);
129 assert!(s.contains("|>"), "pipe missing: {}", s);
130 assert!(s.contains("xs"), "lhs missing: {}", s);
131 }
132 #[test]
133 pub(super) fn test_expr_match() {
134 let e = FSharpExpr::Match(
135 Box::new(FSharpExpr::Var("x".to_string())),
136 vec![
137 (
138 FSharpPattern::Lit("0".to_string()),
139 FSharpExpr::Lit("\"zero\"".to_string()),
140 ),
141 (
142 FSharpPattern::Wildcard,
143 FSharpExpr::Lit("\"nonzero\"".to_string()),
144 ),
145 ],
146 );
147 let s = b().emit_expr(&e, 0);
148 assert!(s.contains("match x with"), "got: {}", s);
149 assert!(s.contains("| 0 -> "), "got: {}", s);
150 assert!(s.contains("| _ -> "), "got: {}", s);
151 }
152 #[test]
153 pub(super) fn test_expr_if() {
154 let e = FSharpExpr::If(
155 Box::new(FSharpExpr::Lit("true".to_string())),
156 Box::new(FSharpExpr::Lit("1".to_string())),
157 Box::new(FSharpExpr::Lit("0".to_string())),
158 );
159 let s = b().emit_expr(&e, 0);
160 assert!(s.contains("if true then"), "got: {}", s);
161 assert!(s.contains("else"), "got: {}", s);
162 }
163 #[test]
164 pub(super) fn test_pattern_ctor() {
165 let p = FSharpPattern::Ctor(
166 "Some".to_string(),
167 vec![FSharpPattern::Var("v".to_string())],
168 );
169 assert_eq!(b().emit_pattern(&p), "Some(v)");
170 }
171 #[test]
172 pub(super) fn test_pattern_cons() {
173 let p = FSharpPattern::Cons(
174 Box::new(FSharpPattern::Var("h".to_string())),
175 Box::new(FSharpPattern::Var("t".to_string())),
176 );
177 assert_eq!(b().emit_pattern(&p), "h :: t");
178 }
179 #[test]
180 pub(super) fn test_emit_record() {
181 let rec = FSharpRecord {
182 name: "Point".to_string(),
183 type_params: vec![],
184 fields: vec![
185 ("x".to_string(), FSharpType::Float),
186 ("y".to_string(), FSharpType::Float),
187 ],
188 doc: None,
189 };
190 let s = b().emit_record(&rec);
191 assert!(s.contains("type Point ="), "got: {}", s);
192 assert!(s.contains("x: float"), "got: {}", s);
193 assert!(s.contains("y: float"), "got: {}", s);
194 }
195 #[test]
196 pub(super) fn test_emit_union() {
197 let union = FSharpUnion {
198 name: "Shape".to_string(),
199 type_params: vec![],
200 cases: vec![
201 FSharpUnionCase {
202 name: "Circle".to_string(),
203 fields: vec![FSharpType::Float],
204 },
205 FSharpUnionCase {
206 name: "Rect".to_string(),
207 fields: vec![FSharpType::Float, FSharpType::Float],
208 },
209 FSharpUnionCase {
210 name: "Point".to_string(),
211 fields: vec![],
212 },
213 ],
214 doc: None,
215 };
216 let s = b().emit_union(&union);
217 assert!(s.contains("type Shape ="), "got: {}", s);
218 assert!(s.contains("| Circle of float"), "got: {}", s);
219 assert!(s.contains("| Rect of float * float"), "got: {}", s);
220 assert!(s.contains("| Point"), "got: {}", s);
221 }
222 #[test]
223 pub(super) fn test_emit_function() {
224 let func = FSharpFunction {
225 name: "add".to_string(),
226 is_recursive: false,
227 is_inline: false,
228 type_params: vec![],
229 params: vec![
230 ("a".to_string(), Some(FSharpType::Int)),
231 ("b".to_string(), Some(FSharpType::Int)),
232 ],
233 return_type: Some(FSharpType::Int),
234 body: FSharpExpr::BinOp(
235 "+".to_string(),
236 Box::new(FSharpExpr::Var("a".to_string())),
237 Box::new(FSharpExpr::Var("b".to_string())),
238 ),
239 doc: None,
240 };
241 let s = b().emit_function(&func);
242 assert!(s.contains("let add"), "got: {}", s);
243 assert!(s.contains("(a: int)"), "got: {}", s);
244 assert!(s.contains(": int"), "got: {}", s);
245 assert!(s.contains("a + b"), "got: {}", s);
246 }
247 #[test]
248 pub(super) fn test_emit_module() {
249 let module = FSharpModule {
250 name: "OxiLean.Math".to_string(),
251 auto_open: false,
252 records: vec![],
253 unions: vec![],
254 functions: vec![FSharpFunction {
255 name: "square".to_string(),
256 is_recursive: false,
257 is_inline: false,
258 type_params: vec![],
259 params: vec![("x".to_string(), Some(FSharpType::Int))],
260 return_type: Some(FSharpType::Int),
261 body: FSharpExpr::BinOp(
262 "*".to_string(),
263 Box::new(FSharpExpr::Var("x".to_string())),
264 Box::new(FSharpExpr::Var("x".to_string())),
265 ),
266 doc: None,
267 }],
268 opens: vec!["System".to_string()],
269 };
270 let s = b().emit_module(&module);
271 assert!(s.contains("module OxiLean.Math"), "got: {}", s);
272 assert!(s.contains("open System"), "got: {}", s);
273 assert!(s.contains("let square"), "got: {}", s);
274 assert!(s.contains("x * x"), "got: {}", s);
275 }
276 #[test]
277 pub(super) fn test_recursive_function() {
278 let func = FSharpFunction {
279 name: "factorial".to_string(),
280 is_recursive: true,
281 is_inline: false,
282 type_params: vec![],
283 params: vec![("n".to_string(), Some(FSharpType::Int))],
284 return_type: Some(FSharpType::Int),
285 body: FSharpExpr::Match(
286 Box::new(FSharpExpr::Var("n".to_string())),
287 vec![
288 (
289 FSharpPattern::Lit("0".to_string()),
290 FSharpExpr::Lit("1".to_string()),
291 ),
292 (
293 FSharpPattern::Var("k".to_string()),
294 FSharpExpr::BinOp(
295 "*".to_string(),
296 Box::new(FSharpExpr::Var("k".to_string())),
297 Box::new(FSharpExpr::App(
298 Box::new(FSharpExpr::Var("factorial".to_string())),
299 Box::new(FSharpExpr::BinOp(
300 "-".to_string(),
301 Box::new(FSharpExpr::Var("k".to_string())),
302 Box::new(FSharpExpr::Lit("1".to_string())),
303 )),
304 )),
305 ),
306 ),
307 ],
308 ),
309 doc: None,
310 };
311 let s = b().emit_function(&func);
312 assert!(s.contains("let rec factorial"), "got: {}", s);
313 assert!(s.contains("match n with"), "got: {}", s);
314 }
315 #[test]
316 pub(super) fn test_expr_ann() {
317 let e = FSharpExpr::Ann(Box::new(FSharpExpr::Lit("42".to_string())), FSharpType::Int);
318 assert_eq!(b().emit_expr(&e, 0), "(42 : int)");
319 }
320 #[test]
321 pub(super) fn test_type_var() {
322 assert_eq!(b().emit_type(&FSharpType::TypeVar("a".to_string())), "'a");
323 }
324}
325#[allow(dead_code)]
327pub fn computation_expr(kind: ComputationExprKind, stmts: Vec<FSharpExpr>) -> FSharpExpr {
328 let _kind_str = kind.to_string();
329 FSharpExpr::Raw(format!(
330 "{} {{\n{}\n}}",
331 _kind_str,
332 stmts
333 .iter()
334 .map(|s| format!(" {}", {
335 let b = FSharpBackend::new();
336 b.emit_expr(s, 1)
337 }))
338 .collect::<Vec<_>>()
339 .join("\n")
340 ))
341}
342#[allow(dead_code)]
344pub fn float_with_measure(value: f64, measure: &str) -> FSharpExpr {
345 FSharpExpr::Lit(format!("{}<{}>", value, measure))
346}
347#[allow(dead_code)]
349pub fn fvar(name: impl Into<String>) -> FSharpExpr {
350 FSharpExpr::Var(name.into())
351}
352#[allow(dead_code)]
354pub fn flit(s: impl Into<String>) -> FSharpExpr {
355 FSharpExpr::Lit(s.into())
356}
357#[allow(dead_code)]
359pub fn fapp(f: FSharpExpr, x: FSharpExpr) -> FSharpExpr {
360 FSharpExpr::App(Box::new(f), Box::new(x))
361}
362#[allow(dead_code)]
364pub fn fapp_multi(f: FSharpExpr, args: Vec<FSharpExpr>) -> FSharpExpr {
365 args.into_iter().fold(f, fapp)
366}
367#[allow(dead_code)]
369pub fn flam(param: impl Into<String>, body: FSharpExpr) -> FSharpExpr {
370 FSharpExpr::Lambda(param.into(), Box::new(body))
371}
372#[allow(dead_code)]
374pub fn flam_multi(params: Vec<impl Into<String>>, body: FSharpExpr) -> FSharpExpr {
375 FSharpExpr::MultiLambda(
376 params.into_iter().map(|p| p.into()).collect(),
377 Box::new(body),
378 )
379}
380#[allow(dead_code)]
382pub fn flet(name: impl Into<String>, val: FSharpExpr, body: FSharpExpr) -> FSharpExpr {
383 FSharpExpr::Let(name.into(), Box::new(val), Box::new(body))
384}
385#[allow(dead_code)]
387pub fn fletrec(name: impl Into<String>, val: FSharpExpr, body: FSharpExpr) -> FSharpExpr {
388 FSharpExpr::LetRec(name.into(), Box::new(val), Box::new(body))
389}
390#[allow(dead_code)]
392pub fn fif(cond: FSharpExpr, then_e: FSharpExpr, else_e: FSharpExpr) -> FSharpExpr {
393 FSharpExpr::If(Box::new(cond), Box::new(then_e), Box::new(else_e))
394}
395#[allow(dead_code)]
397pub fn fbinop(op: impl Into<String>, lhs: FSharpExpr, rhs: FSharpExpr) -> FSharpExpr {
398 FSharpExpr::BinOp(op.into(), Box::new(lhs), Box::new(rhs))
399}
400#[allow(dead_code)]
402pub fn funary(op: impl Into<String>, operand: FSharpExpr) -> FSharpExpr {
403 FSharpExpr::UnaryOp(op.into(), Box::new(operand))
404}
405#[allow(dead_code)]
407pub fn fpipe(lhs: FSharpExpr, rhs: FSharpExpr) -> FSharpExpr {
408 FSharpExpr::Pipe(Box::new(lhs), Box::new(rhs))
409}
410#[allow(dead_code)]
412pub fn fpipe_chain(init: FSharpExpr, funcs: Vec<FSharpExpr>) -> FSharpExpr {
413 funcs.into_iter().fold(init, fpipe)
414}
415#[allow(dead_code)]
417pub fn fctor(name: impl Into<String>, args: Vec<FSharpExpr>) -> FSharpExpr {
418 FSharpExpr::Ctor(name.into(), args)
419}
420#[allow(dead_code)]
422pub fn fsome(x: FSharpExpr) -> FSharpExpr {
423 fctor("Some", vec![x])
424}
425#[allow(dead_code)]
427pub fn fnone() -> FSharpExpr {
428 fvar("None")
429}
430#[allow(dead_code)]
432pub fn fok(x: FSharpExpr) -> FSharpExpr {
433 fctor("Ok", vec![x])
434}
435#[allow(dead_code)]
437pub fn ferror(e: FSharpExpr) -> FSharpExpr {
438 fctor("Error", vec![e])
439}
440#[allow(dead_code)]
442pub fn fseq(e1: FSharpExpr, e2: FSharpExpr) -> FSharpExpr {
443 FSharpExpr::Seq(Box::new(e1), Box::new(e2))
444}
445#[allow(dead_code)]
447pub fn ffield(base: FSharpExpr, field: impl Into<String>) -> FSharpExpr {
448 FSharpExpr::FieldAccess(Box::new(base), field.into())
449}
450#[allow(dead_code)]
452pub fn fann(expr: FSharpExpr, ty: FSharpType) -> FSharpExpr {
453 FSharpExpr::Ann(Box::new(expr), ty)
454}
455#[allow(dead_code)]
457pub fn fint(n: i64) -> FSharpExpr {
458 FSharpExpr::Lit(n.to_string())
459}
460#[allow(dead_code)]
462pub fn ffloat(x: f64) -> FSharpExpr {
463 FSharpExpr::Lit(format!("{}", x))
464}
465#[allow(dead_code)]
467pub fn fbool(b: bool) -> FSharpExpr {
468 FSharpExpr::Lit(if b { "true" } else { "false" }.to_string())
469}
470#[allow(dead_code)]
472pub fn fstring(s: impl Into<String>) -> FSharpExpr {
473 FSharpExpr::Lit(format!("\"{}\"", s.into()))
474}
475#[allow(dead_code)]
477pub fn funit() -> FSharpExpr {
478 FSharpExpr::Lit("()".to_string())
479}
480#[allow(dead_code)]
482pub fn pwild() -> FSharpPattern {
483 FSharpPattern::Wildcard
484}
485#[allow(dead_code)]
487pub fn pvar(name: impl Into<String>) -> FSharpPattern {
488 FSharpPattern::Var(name.into())
489}
490#[allow(dead_code)]
492pub fn plit(s: impl Into<String>) -> FSharpPattern {
493 FSharpPattern::Lit(s.into())
494}
495#[allow(dead_code)]
497pub fn psome(inner: FSharpPattern) -> FSharpPattern {
498 FSharpPattern::Ctor("Some".to_string(), vec![inner])
499}
500#[allow(dead_code)]
502pub fn pnone() -> FSharpPattern {
503 FSharpPattern::Ctor("None".to_string(), vec![])
504}
505#[allow(dead_code)]
507pub fn pok(inner: FSharpPattern) -> FSharpPattern {
508 FSharpPattern::Ctor("Ok".to_string(), vec![inner])
509}
510#[allow(dead_code)]
512pub fn perror(inner: FSharpPattern) -> FSharpPattern {
513 FSharpPattern::Ctor("Error".to_string(), vec![inner])
514}
515#[allow(dead_code)]
517pub fn ptuple(pats: Vec<FSharpPattern>) -> FSharpPattern {
518 FSharpPattern::Tuple(pats)
519}
520#[allow(dead_code)]
522pub fn pcons(head: FSharpPattern, tail: FSharpPattern) -> FSharpPattern {
523 FSharpPattern::Cons(Box::new(head), Box::new(tail))
524}
525#[allow(dead_code)]
527pub fn pas(pat: FSharpPattern, name: impl Into<String>) -> FSharpPattern {
528 FSharpPattern::As(Box::new(pat), name.into())
529}
530#[allow(dead_code)]
532pub fn pwhen(pat: FSharpPattern, guard: FSharpExpr) -> FSharpPattern {
533 FSharpPattern::When(Box::new(pat), Box::new(guard))
534}
535#[allow(dead_code)]
537pub fn fs_int_list() -> FSharpType {
538 FSharpType::List(Box::new(FSharpType::Int))
539}
540#[allow(dead_code)]
542pub fn fs_string_list() -> FSharpType {
543 FSharpType::List(Box::new(FSharpType::FsString))
544}
545#[allow(dead_code)]
547pub fn fs_int_option() -> FSharpType {
548 FSharpType::Option(Box::new(FSharpType::Int))
549}
550#[allow(dead_code)]
552pub fn fs_array(ty: FSharpType) -> FSharpType {
553 FSharpType::Array(Box::new(ty))
554}
555#[allow(dead_code)]
557pub fn fs_map(key: FSharpType, value: FSharpType) -> FSharpType {
558 FSharpType::Generic("Map".to_string(), vec![key, value])
559}
560#[allow(dead_code)]
562pub fn fs_set(elem: FSharpType) -> FSharpType {
563 FSharpType::Generic("Set".to_string(), vec![elem])
564}
565#[allow(dead_code)]
567pub fn fs_result(ok: FSharpType, err: FSharpType) -> FSharpType {
568 FSharpType::Result(Box::new(ok), Box::new(err))
569}
570#[allow(dead_code)]
572pub fn fs_fun(from: FSharpType, to: FSharpType) -> FSharpType {
573 FSharpType::Fun(Box::new(from), Box::new(to))
574}
575#[allow(dead_code)]
577pub fn fs_tuple(tys: Vec<FSharpType>) -> FSharpType {
578 FSharpType::Tuple(tys)
579}
580#[allow(dead_code)]
582pub fn collect_module_metrics(module: &FSharpModule) -> FSharpModuleMetrics {
583 let mut metrics = FSharpModuleMetrics::default();
584 metrics.type_count = module.records.len() + module.unions.len();
585 metrics.function_count = module.functions.len();
586 for f in &module.functions {
587 if f.is_recursive {
588 metrics.recursive_count += 1;
589 }
590 if f.is_inline {
591 metrics.inline_count += 1;
592 }
593 }
594 metrics.estimated_lines = 5
595 + module.opens.len()
596 + module.records.len() * 4
597 + module.unions.len() * 5
598 + module.functions.len() * 6;
599 metrics
600}
601#[allow(dead_code)]
603pub fn async_return(x: FSharpExpr) -> FSharpExpr {
604 FSharpExpr::Raw(format!(
605 "async {{ return! {} }}",
606 FSharpBackend::new().emit_expr(&x, 0)
607 ))
608}
609#[allow(dead_code)]
611pub fn async_let_bang(name: impl Into<String>, comp: FSharpExpr, body: FSharpExpr) -> FSharpExpr {
612 let b = FSharpBackend::new();
613 FSharpExpr::Raw(format!(
614 "async {{\n let! {} = {}\n return! {}\n}}",
615 name.into(),
616 b.emit_expr(&comp, 1),
617 b.emit_expr(&body, 1)
618 ))
619}
620#[allow(dead_code)]
622pub fn async_map(f: FSharpExpr, comp: FSharpExpr) -> FSharpExpr {
623 fapp_multi(fvar("Async.map"), vec![f, comp])
624}
625#[allow(dead_code)]
627pub fn async_bind(f: FSharpExpr, comp: FSharpExpr) -> FSharpExpr {
628 fapp_multi(fvar("Async.bind"), vec![f, comp])
629}
630#[cfg(test)]
631mod extended_fsharp_tests {
632 use super::*;
633 #[test]
634 pub(super) fn test_attribute_simple() {
635 let a = FSharpAttribute::simple("Serializable");
636 assert_eq!(a.emit(), "[<Serializable>]");
637 }
638 #[test]
639 pub(super) fn test_attribute_with_args() {
640 let a = FSharpAttribute::with_args("DllImport", vec!["\"mylib.dll\"".to_string()]);
641 assert!(a.emit().contains("DllImport"));
642 assert!(a.emit().contains("mylib.dll"));
643 }
644 #[test]
645 pub(super) fn test_type_alias_emit() {
646 let alias = FSharpTypeAlias {
647 name: "IntList".to_string(),
648 type_params: vec![],
649 aliased_type: FSharpType::List(Box::new(FSharpType::Int)),
650 doc: None,
651 };
652 let s = alias.emit();
653 assert!(s.contains("type IntList = "));
654 assert!(s.contains("int list"));
655 }
656 #[test]
657 pub(super) fn test_type_alias_generic() {
658 let alias = FSharpTypeAlias {
659 name: "Pair".to_string(),
660 type_params: vec!["'a".to_string(), "'b".to_string()],
661 aliased_type: FSharpType::Tuple(vec![
662 FSharpType::TypeVar("a".to_string()),
663 FSharpType::TypeVar("b".to_string()),
664 ]),
665 doc: None,
666 };
667 let s = alias.emit();
668 assert!(s.contains("type Pair<"));
669 assert!(s.contains("'a * 'b"));
670 }
671 #[test]
672 pub(super) fn test_fvar_flit() {
673 let b = FSharpBackend::new();
674 assert_eq!(b.emit_expr(&fvar("x"), 0), "x");
675 assert_eq!(b.emit_expr(&flit("42"), 0), "42");
676 }
677 #[test]
678 pub(super) fn test_fapp_multi() {
679 let b = FSharpBackend::new();
680 let e = fapp_multi(fvar("f"), vec![fvar("x"), fvar("y")]);
681 let s = b.emit_expr(&e, 0);
682 assert!(s.contains("f"));
683 assert!(s.contains("x"));
684 assert!(s.contains("y"));
685 }
686 #[test]
687 pub(super) fn test_fpipe_chain() {
688 let b = FSharpBackend::new();
689 let e = fpipe_chain(fvar("xs"), vec![fvar("List.sort"), fvar("List.rev")]);
690 let s = b.emit_expr(&e, 0);
691 assert!(s.contains("|>"));
692 assert!(s.contains("xs"));
693 }
694 #[test]
695 pub(super) fn test_fsome_fnone() {
696 let b = FSharpBackend::new();
697 let s = b.emit_expr(&fsome(fvar("x")), 0);
698 assert!(s.contains("Some"));
699 let n = b.emit_expr(&fnone(), 0);
700 assert_eq!(n, "None");
701 }
702 #[test]
703 pub(super) fn test_fok_ferror() {
704 let b = FSharpBackend::new();
705 let ok = b.emit_expr(&fok(fvar("v")), 0);
706 assert!(ok.contains("Ok"));
707 let err = b.emit_expr(&ferror(fvar("e")), 0);
708 assert!(err.contains("Error"));
709 }
710 #[test]
711 pub(super) fn test_fint_ffloat() {
712 let b = FSharpBackend::new();
713 assert_eq!(b.emit_expr(&fint(42), 0), "42");
714 assert!(b.emit_expr(&ffloat(3.14), 0).contains("3.14"));
715 }
716 #[test]
717 pub(super) fn test_fbool_fstring_funit() {
718 let b = FSharpBackend::new();
719 assert_eq!(b.emit_expr(&fbool(true), 0), "true");
720 assert_eq!(b.emit_expr(&fbool(false), 0), "false");
721 assert!(b.emit_expr(&fstring("hello"), 0).contains("hello"));
722 assert_eq!(b.emit_expr(&funit(), 0), "()");
723 }
724 #[test]
725 pub(super) fn test_pattern_psome_pnone() {
726 let b = FSharpBackend::new();
727 let p = psome(pvar("v"));
728 assert!(b.emit_pattern(&p).contains("Some"));
729 let n = pnone();
730 assert!(b.emit_pattern(&n).contains("None"));
731 }
732 #[test]
733 pub(super) fn test_pattern_pok_perror() {
734 let b = FSharpBackend::new();
735 let p = pok(pvar("x"));
736 assert!(b.emit_pattern(&p).contains("Ok"));
737 let e = perror(pvar("err"));
738 assert!(b.emit_pattern(&e).contains("Error"));
739 }
740 #[test]
741 pub(super) fn test_pattern_ptuple() {
742 let b = FSharpBackend::new();
743 let p = ptuple(vec![pvar("a"), pvar("b")]);
744 let s = b.emit_pattern(&p);
745 assert!(s.contains("a"));
746 assert!(s.contains("b"));
747 assert!(s.contains("("));
748 }
749 #[test]
750 pub(super) fn test_pattern_pcons() {
751 let b = FSharpBackend::new();
752 let p = pcons(pvar("h"), pvar("t"));
753 let s = b.emit_pattern(&p);
754 assert!(s.contains("h :: t"));
755 }
756 #[test]
757 pub(super) fn test_fs_type_constructors() {
758 assert!(format!("{}", fs_int_list()).contains("int list"));
759 assert!(format!("{}", fs_string_list()).contains("string list"));
760 assert!(format!("{}", fs_int_option()).contains("int option"));
761 assert!(format!("{}", fs_array(FSharpType::Float)).contains("array"));
762 assert!(format!("{}", fs_map(FSharpType::FsString, FSharpType::Int)).contains("Map"));
763 assert!(format!("{}", fs_set(FSharpType::Int)).contains("Set"));
764 assert!(format!("{}", fs_result(FSharpType::Int, FSharpType::FsString)).contains("Result"));
765 }
766 #[test]
767 pub(super) fn test_fs_fun_type() {
768 let ty = fs_fun(FSharpType::Int, FSharpType::Bool);
769 assert!(format!("{}", ty).contains("->"));
770 }
771 #[test]
772 pub(super) fn test_fs_tuple_type() {
773 let ty = fs_tuple(vec![FSharpType::Int, FSharpType::Float, FSharpType::Bool]);
774 let s = format!("{}", ty);
775 assert!(s.contains("int"));
776 assert!(s.contains("float"));
777 assert!(s.contains("bool"));
778 assert!(s.contains("*"));
779 }
780 #[test]
781 pub(super) fn test_snippets() {
782 assert!(FSharpSnippets::option_map().contains("Some"));
783 assert!(FSharpSnippets::option_bind().contains("None"));
784 assert!(FSharpSnippets::result_map().contains("Ok"));
785 assert!(FSharpSnippets::list_fold().contains("foldLeft"));
786 assert!(FSharpSnippets::memoize().contains("Dictionary"));
787 assert!(FSharpSnippets::fix_point().contains("fix"));
788 }
789 #[test]
790 pub(super) fn test_numeric_helpers() {
791 let b = FSharpBackend::new();
792 let clamp = FSharpNumericHelpers::clamp();
793 let s = b.emit_function(&clamp);
794 assert!(s.contains("clamp"));
795 assert!(s.contains("inline"));
796 let sq = FSharpNumericHelpers::square();
797 let s2 = b.emit_function(&sq);
798 assert!(s2.contains("square"));
799 let pow = FSharpNumericHelpers::pow_int();
800 let s3 = b.emit_function(&pow);
801 assert!(s3.contains("powInt"));
802 assert!(s3.contains("rec"));
803 let gcd = FSharpNumericHelpers::gcd();
804 let s4 = b.emit_function(&gcd);
805 assert!(s4.contains("gcd"));
806 }
807 #[test]
808 pub(super) fn test_module_builder() {
809 let s = FSharpModuleBuilder::new("MyLib")
810 .open("System")
811 .function(FSharpNumericHelpers::square())
812 .emit();
813 assert!(s.contains("module MyLib"));
814 assert!(s.contains("open System"));
815 assert!(s.contains("square"));
816 }
817 #[test]
818 pub(super) fn test_function_builder() {
819 let f = FSharpFunctionBuilder::new("double")
820 .param("x", Some(FSharpType::Int))
821 .returns(FSharpType::Int)
822 .body(fbinop("*", flit("2"), fvar("x")))
823 .doc("Double an integer.")
824 .build();
825 let b = FSharpBackend::new();
826 let s = b.emit_function(&f);
827 assert!(s.contains("double"));
828 assert!(s.contains("2 * x"));
829 }
830 #[test]
831 pub(super) fn test_collect_module_metrics() {
832 let module = FSharpModule {
833 name: "Test".to_string(),
834 auto_open: false,
835 records: vec![FSharpRecord {
836 name: "Point".to_string(),
837 type_params: vec![],
838 fields: vec![("x".to_string(), FSharpType::Float)],
839 doc: None,
840 }],
841 unions: vec![],
842 functions: vec![FSharpNumericHelpers::square(), FSharpNumericHelpers::gcd()],
843 opens: vec!["System".to_string()],
844 };
845 let m = collect_module_metrics(&module);
846 assert_eq!(m.type_count, 1);
847 assert_eq!(m.function_count, 2);
848 assert_eq!(m.recursive_count, 1);
849 assert_eq!(m.inline_count, 1);
850 assert!(m.estimated_lines > 0);
851 }
852 #[test]
853 pub(super) fn test_stdlib_id() {
854 let b = FSharpBackend::new();
855 let f = FSharpStdLib::id_function();
856 let s = b.emit_function(&f);
857 assert!(s.contains("id"));
858 }
859 #[test]
860 pub(super) fn test_stdlib_flip() {
861 let b = FSharpBackend::new();
862 let f = FSharpStdLib::flip_function();
863 let s = b.emit_function(&f);
864 assert!(s.contains("flip"));
865 }
866 #[test]
867 pub(super) fn test_stdlib_const() {
868 let b = FSharpBackend::new();
869 let f = FSharpStdLib::const_function();
870 let s = b.emit_function(&f);
871 assert!(s.contains("konst"));
872 }
873 #[test]
874 pub(super) fn test_stdlib_foldl() {
875 let b = FSharpBackend::new();
876 let f = FSharpStdLib::foldl_function();
877 let s = b.emit_function(&f);
878 assert!(s.contains("foldl"));
879 assert!(s.contains("rec"));
880 }
881 #[test]
882 pub(super) fn test_stdlib_filter() {
883 let b = FSharpBackend::new();
884 let f = FSharpStdLib::filter_function();
885 let s = b.emit_function(&f);
886 assert!(s.contains("filterList"));
887 }
888 #[test]
889 pub(super) fn test_stdlib_zip_with() {
890 let b = FSharpBackend::new();
891 let f = FSharpStdLib::zip_with_function();
892 let s = b.emit_function(&f);
893 assert!(s.contains("zipWith"));
894 }
895 #[test]
896 pub(super) fn test_union_case_named() {
897 let c = FSharpUnionCaseNamed {
898 name: "Circle".to_string(),
899 named_fields: vec![("radius".to_string(), FSharpType::Float)],
900 };
901 let s = c.emit();
902 assert!(s.contains("Circle"));
903 assert!(s.contains("radius: float"));
904 }
905 #[test]
906 pub(super) fn test_measure_decl() {
907 let m = FSharpMeasure {
908 name: "kg".to_string(),
909 abbrev: None,
910 };
911 let s = m.emit();
912 assert!(s.contains("[<Measure>]"));
913 assert!(s.contains("type kg"));
914 }
915 #[test]
916 pub(super) fn test_float_with_measure() {
917 let b = FSharpBackend::new();
918 let e = float_with_measure(42.0, "kg");
919 let s = b.emit_expr(&e, 0);
920 assert!(s.contains("kg"));
921 }
922 #[test]
923 pub(super) fn test_computation_expr() {
924 let e = computation_expr(ComputationExprKind::Async, vec![fvar("doSomething")]);
925 let b = FSharpBackend::new();
926 let s = b.emit_expr(&e, 0);
927 assert!(s.contains("async"));
928 assert!(s.contains("doSomething"));
929 }
930 #[test]
931 pub(super) fn test_mutual_group_emit() {
932 let f1 = FSharpFunctionBuilder::new("isEven")
933 .recursive()
934 .param("n", Some(FSharpType::Int))
935 .returns(FSharpType::Bool)
936 .body(fif(
937 fbinop("=", fvar("n"), fint(0)),
938 fbool(true),
939 fapp(fvar("isOdd"), fbinop("-", fvar("n"), fint(1))),
940 ))
941 .build();
942 let f2 = FSharpFunctionBuilder::new("isOdd")
943 .recursive()
944 .param("n", Some(FSharpType::Int))
945 .returns(FSharpType::Bool)
946 .body(fif(
947 fbinop("=", fvar("n"), fint(0)),
948 fbool(false),
949 fapp(fvar("isEven"), fbinop("-", fvar("n"), fint(1))),
950 ))
951 .build();
952 let group = FSharpMutualGroup {
953 functions: vec![f1, f2],
954 };
955 let b = FSharpBackend::new();
956 let s = group.emit(&b);
957 assert!(s.contains("isEven"));
958 assert!(s.contains("isOdd"));
959 }
960 #[test]
961 pub(super) fn test_interface_emit() {
962 let iface = FSharpInterface {
963 name: "IComparable".to_string(),
964 type_params: vec!["'T".to_string()],
965 methods: vec![(
966 "CompareTo".to_string(),
967 vec![("other".to_string(), FSharpType::TypeVar("T".to_string()))],
968 FSharpType::Int,
969 )],
970 properties: vec![],
971 doc: None,
972 };
973 let s = iface.emit();
974 assert!(s.contains("IComparable"));
975 assert!(s.contains("abstract member CompareTo"));
976 }
977 #[test]
978 pub(super) fn test_active_pattern_total() {
979 let ap = FSharpActivePattern {
980 kind: ActivePatternKind::Total(vec!["Even".to_string(), "Odd".to_string()]),
981 params: vec!["n".to_string()],
982 body: fif(
983 fbinop("=", fbinop("%", fvar("n"), fint(2)), fint(0)),
984 fvar("Even"),
985 fvar("Odd"),
986 ),
987 };
988 let b = FSharpBackend::new();
989 let s = ap.emit(&b);
990 assert!(s.contains("Even|Odd"));
991 assert!(s.contains("let"));
992 }
993 #[test]
994 pub(super) fn test_active_pattern_partial() {
995 let ap = FSharpActivePattern {
996 kind: ActivePatternKind::Partial("IsPositive".to_string()),
997 params: vec!["x".to_string()],
998 body: fif(fbinop(">", fvar("x"), fint(0)), fsome(fvar("x")), fnone()),
999 };
1000 let b = FSharpBackend::new();
1001 let s = ap.emit(&b);
1002 assert!(s.contains("IsPositive"));
1003 assert!(s.contains("|_|"));
1004 }
1005 #[test]
1006 pub(super) fn test_async_return() {
1007 let b = FSharpBackend::new();
1008 let e = async_return(fvar("result"));
1009 let s = b.emit_expr(&e, 0);
1010 assert!(s.contains("async"));
1011 assert!(s.contains("result"));
1012 }
1013 #[test]
1014 pub(super) fn test_async_map_bind() {
1015 let b = FSharpBackend::new();
1016 let e = async_map(fvar("f"), fvar("comp"));
1017 let s = b.emit_expr(&e, 0);
1018 assert!(s.contains("Async.map"));
1019 let e2 = async_bind(fvar("f"), fvar("comp"));
1020 let s2 = b.emit_expr(&e2, 0);
1021 assert!(s2.contains("Async.bind"));
1022 }
1023 #[test]
1024 pub(super) fn test_auto_open_module() {
1025 let s = FSharpModuleBuilder::new("Operators").auto_open().emit();
1026 assert!(s.contains("[<AutoOpen>]"));
1027 assert!(s.contains("module Operators"));
1028 }
1029}