1pub mod translatable;
2
3use crate::components::context::config::Environment;
4use crate::components::context::Context;
5use crate::components::error_message::help_data::HelpData;
6use crate::components::language::argument_value::ArgumentValue;
7use crate::components::language::format_backtick;
8use crate::components::language::function_lang::Function;
9use crate::components::language::operators::Op;
10use crate::components::language::set_related_type_if_variable;
11use crate::components::language::var::Var;
12use crate::components::language::Lang;
13use crate::components::language::ModulePosition;
14use crate::components::r#type::argument_type::ArgumentType;
15use crate::components::r#type::array_type::ArrayType;
16use crate::components::r#type::function_type::FunctionType;
17use crate::components::r#type::vector_type::VecType;
18use crate::components::r#type::Type;
19use crate::processes::transpiling::translatable::Translatable;
20use crate::processes::type_checking::flatten_operator_union;
21use crate::processes::type_checking::type_comparison::reduce_type;
22use crate::processes::type_checking::typing;
23use translatable::RTranslatable;
24
25#[cfg(not(feature = "wasm"))]
26use std::fs::File;
27#[cfg(not(feature = "wasm"))]
28use std::io::Write;
29#[cfg(not(feature = "wasm"))]
30use std::path::PathBuf;
31
32use std::cell::RefCell;
33use std::collections::HashMap;
34
35thread_local! {
37 static GENERATED_FILES: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
38}
39
40pub fn register_generated_file(path: &str, content: &str) {
42 GENERATED_FILES.with(|files| {
43 files
44 .borrow_mut()
45 .insert(path.to_string(), content.to_string());
46 });
47}
48
49pub fn get_generated_files() -> HashMap<String, String> {
51 GENERATED_FILES.with(|files| files.borrow().clone())
52}
53
54pub fn clear_generated_files() {
56 GENERATED_FILES.with(|files| {
57 files.borrow_mut().clear();
58 });
59}
60
61#[cfg(not(feature = "wasm"))]
63fn write_output_file(path: &str, content: &str) -> Result<(), String> {
64 use std::fs;
65
66 register_generated_file(path, content);
68
69 let path_buf = PathBuf::from(path);
70 if let Some(parent) = path_buf.parent() {
71 fs::create_dir_all(parent).map_err(|e| e.to_string())?;
72 }
73 let mut file = File::create(&path_buf).map_err(|e| e.to_string())?;
74 file.write_all(content.as_bytes())
75 .map_err(|e| e.to_string())?;
76 Ok(())
77}
78
79#[cfg(feature = "wasm")]
80fn write_output_file(path: &str, content: &str) -> Result<(), String> {
81 register_generated_file(path, content);
82 Ok(())
83}
84
85pub trait ToSome {
86 fn to_some(self) -> Option<Self>
87 where
88 Self: Sized;
89}
90
91impl<T: Sized> ToSome for T {
92 fn to_some(self) -> Option<Self> {
93 Some(self)
94 }
95}
96
97trait AndIf {
98 fn and_if<F>(self, condition: F) -> Option<Self>
99 where
100 F: Fn(Self) -> bool,
101 Self: Sized;
102}
103
104impl<T: Clone> AndIf for T {
105 fn and_if<F>(self, condition: F) -> Option<Self>
106 where
107 F: Fn(Self) -> bool,
108 {
109 if condition(self.clone()) {
110 Some(self)
111 } else {
112 None
113 }
114 }
115}
116
117const JS_HEADER: &str = "";
118
119fn to_pattern_match_statement(
120 exp: Lang,
121 branches: &[(Lang, Box<Lang>)],
122 context: &Context,
123) -> String {
124 let match_var = "match_val__";
125 let res = branches
126 .iter()
127 .enumerate()
128 .map(|(id, (pattern, body))| {
129 let (cond, bindings) = pattern_to_condition(pattern, match_var, context);
130 let body_str = body.to_r(context).0;
131 let body_with_bindings = if bindings.is_empty() {
132 body_str
133 } else {
134 format!("{}\n{}", bindings, body_str)
135 };
136 if cond == "TRUE" {
137 if id == 0 {
139 format!("{{\n{}\n}}", body_with_bindings)
140 } else {
141 format!("else {{\n{}\n}}", body_with_bindings)
142 }
143 } else if id == 0 {
144 format!("if ({}) {{\n{}\n}}", cond, body_with_bindings)
145 } else {
146 format!("else if ({}) {{\n{}\n}}", cond, body_with_bindings)
147 }
148 })
149 .collect::<Vec<_>>()
150 .join(" ");
151 format!("{{\n{} <- {}\n{}\n}}", match_var, exp.to_r(context).0, res)
152}
153
154fn type_to_r_check(typ: &Type) -> Option<&'static str> {
156 match typ {
157 Type::Integer(_, _) => Some("is.integer"),
158 Type::Boolean(_) => Some("is.logical"),
159 Type::Number(_) => Some("is.numeric"),
160 Type::Char(_, _) => Some("is.character"),
161 Type::Null(_) => Some("is.null"),
162 _ => None,
163 }
164}
165
166fn pattern_to_condition(pattern: &Lang, match_var: &str, _context: &Context) -> (String, String) {
167 match pattern {
168 Lang::Tag {
170 name, value: inner, ..
171 } => {
172 let cond = format!("{}[[1]] == '{}'", match_var, name);
173 match inner.as_ref() {
174 Lang::Variable { name: var_name, .. } => {
175 let binding = format!("{} <- {}[[\"body\"]]", var_name, match_var);
176 (cond, binding)
177 }
178 Lang::Empty(_) => (cond, String::new()),
179 _ => (cond, String::new()),
180 }
181 }
182 Lang::TypePattern {
184 variable_name: var_name,
185 matched_type: typ,
186 ..
187 } => {
188 let check_fn = type_to_r_check(typ).unwrap_or("is.logical");
189 let cond = format!("{}({})", check_fn, match_var);
190 let binding = format!("{} <- {}", var_name, match_var);
191 (cond, binding)
192 }
193 Lang::Tuple {
195 value: elements, ..
196 } => {
197 let cond = format!(
198 "inherits({}, 'Tuple') && length({}) == {}",
199 match_var,
200 match_var,
201 elements.len()
202 );
203 let bindings: Vec<String> = elements
204 .iter()
205 .enumerate()
206 .filter_map(|(i, elem)| {
207 if let Lang::Variable { name: var_name, .. } = elem {
208 if var_name == "_" {
209 None
210 } else {
211 Some(format!("{} <- {}[[{}]]", var_name, match_var, i + 1))
212 }
213 } else {
214 None
215 }
216 })
217 .collect();
218 (cond, bindings.join("\n"))
219 }
220 Lang::List { value: fields, .. } => {
222 let conditions: Vec<String> = fields
223 .iter()
224 .map(|arg_val: &ArgumentValue| {
225 format!("!is.null({}[[\"{}\"]])", match_var, arg_val.get_argument())
226 })
227 .collect();
228 let cond = if conditions.is_empty() {
229 "is.list(".to_string() + match_var + ")"
230 } else {
231 format!("is.list({}) && {}", match_var, conditions.join(" && "))
232 };
233 let bindings: Vec<String> = fields
234 .iter()
235 .filter_map(|arg_val| {
236 if let Lang::Variable { name: var_name, .. } = &arg_val.get_value() {
237 Some(format!(
238 "{} <- {}[[\"{}\"]]",
239 var_name,
240 match_var,
241 arg_val.get_argument()
242 ))
243 } else {
244 None
245 }
246 })
247 .collect();
248 (cond, bindings.join("\n"))
249 }
250 Lang::DataFrame { value: fields, .. } => {
252 let conditions: Vec<String> = fields
253 .iter()
254 .map(|arg_val: &ArgumentValue| {
255 format!("!is.null({}[[\"{}\"]])", match_var, arg_val.get_argument())
256 })
257 .collect();
258 let cond = if conditions.is_empty() {
259 "is.data.frame(".to_string() + match_var + ")"
260 } else {
261 format!(
262 "is.data.frame({}) && {}",
263 match_var,
264 conditions.join(" && ")
265 )
266 };
267 let bindings: Vec<String> = fields
268 .iter()
269 .filter_map(|arg_val| {
270 if let Lang::Variable { name: var_name, .. } = &arg_val.get_value() {
271 Some(format!(
272 "{} <- {}[[\"{}\"]]",
273 var_name,
274 match_var,
275 arg_val.get_argument()
276 ))
277 } else {
278 None
279 }
280 })
281 .collect();
282 (cond, bindings.join("\n"))
283 }
284 Lang::Variable { name, .. } if name == "_" => ("TRUE".to_string(), String::new()),
286 Lang::Variable { name, .. } => {
288 let binding = format!("{} <- {}", name, match_var);
289 ("TRUE".to_string(), binding)
290 }
291 _ => ("TRUE".to_string(), String::new()),
292 }
293}
294
295impl RTranslatable<(String, Context)> for Lang {
296 fn to_r(&self, cont: &Context) -> (String, Context) {
297 let result = match self {
298 Lang::Bool { value: b, .. } => {
299 let (typ, _, _) = typing(cont, self).to_tuple();
300 let anotation = cont.get_type_anotation(&typ);
301 (
302 format!("{} |> {}", b.to_string().to_uppercase(), anotation),
303 cont.clone(),
304 )
305 }
306 Lang::Number { value: n, .. } => {
307 let (typ, _, _) = typing(cont, self).to_tuple();
308 let anotation = cont.get_type_anotation(&typ);
309 (format!("{} |> {}", n, anotation), cont.clone())
310 }
311 Lang::Integer { value: i, .. } => {
312 let (typ, _, _) = typing(cont, self).to_tuple();
313 let anotation = cont.get_type_anotation(&typ);
314 (format!("{}L |> {}", i, anotation), cont.clone())
315 }
316 Lang::Char { value: s, .. } => {
317 let (typ, _, _) = typing(cont, self).to_tuple();
318 let anotation = cont.get_type_anotation(&typ);
319 (format!("'{}' |> {}", s, anotation), cont.clone())
320 }
321 Lang::Operator {
322 operator: Op::Dot(_),
323 rhs: e1,
324 lhs: e2,
325 ..
326 }
327 | Lang::Operator {
328 operator: Op::Pipe(_),
329 rhs: e1,
330 lhs: e2,
331 ..
332 } => {
333 let e1 = (**e1).clone();
334 let e2 = (**e2).clone();
335 match e2.clone() {
336 Lang::Variable { .. } => match e1 {
337 Lang::Integer { .. } => Translatable::from(cont.clone())
338 .to_r(&e2)
339 .add("[[")
340 .to_r(&e1)
341 .add("]]")
342 .into(),
343 _ => Translatable::from(cont.clone())
344 .to_r(&e2)
345 .add("[['")
346 .to_r(&e1)
347 .add("']]")
348 .into(),
349 },
350 Lang::List { value: fields, .. } => {
351 let at = fields[0].clone();
352 Translatable::from(cont.clone())
353 .add("within(")
354 .to_r(&e2)
355 .add(", { ")
356 .add(&at.get_argument())
357 .add(" <- ")
358 .to_r(&at.get_value())
359 .add(" })")
360 .into()
361 }
362 Lang::DataFrame { value: fields, .. } => {
363 let at = fields[0].clone();
364 Translatable::from(cont.clone())
365 .add("within(")
366 .to_r(&e2)
367 .add(", { ")
368 .add(&at.get_argument())
369 .add(" <- ")
370 .to_r(&at.get_value())
371 .add(" })")
372 .into()
373 }
374 Lang::FunctionApp {
375 identifier: var,
376 arguments: v,
377 help_data: h,
378 } => {
379 let v = [e1].iter().chain(v.iter()).cloned().collect();
380 Lang::FunctionApp {
381 identifier: var,
382 arguments: v,
383 help_data: h,
384 }
385 .to_r(cont)
386 }
387 _ => Translatable::from(cont.clone())
388 .to_r(&e2)
389 .add("[[")
390 .add("]]")
391 .to_r(&e1)
392 .into(),
393 }
394 }
395 Lang::Operator {
396 operator: Op::Dollar(_),
397 rhs: e1,
398 lhs: e2,
399 ..
400 } => {
401 let e1 = (**e1).clone();
402 let e2 = (**e2).clone();
403 let t1 = typing(cont, &e1).value;
404 let val = match (t1.clone(), e2.clone()) {
405 (Type::Vec(vtype, _, _, _), Lang::Variable { name, .. })
406 if vtype.is_array() =>
407 {
408 format!("vec_apply(get, {}, typed_vec('{}'))", e1.to_r(cont).0, name)
409 }
410 (Type::Vec(VecType::S3, _, _, _), Lang::Variable { name, .. }) => {
411 let name_str = name.replace("__", ".");
412 format!("get({}, '{}')", e1.to_r(cont).0, name_str)
413 }
414 (_, Lang::Variable { name, .. }) => format!("{}${}", e1.to_r(cont).0, name),
415 _ => format!("{}${}", e1.to_r(cont).0, e2.to_r(cont).0),
416 };
417 (val, cont.clone())
418 }
419 Lang::Operator {
420 operator: op,
421 rhs: e1,
422 lhs: e2,
423 ..
424 } => {
425 let op_str = format!(" {} ", op);
426 Translatable::from(cont.clone())
427 .to_r(e1)
428 .add(&op_str)
429 .to_r(e2)
430 .into()
431 }
432 Lang::Scope { body: exps, .. } => Translatable::from(cont.clone())
433 .add("{\n")
434 .join(exps, "\n")
435 .add("\n}")
436 .into(),
437 Lang::Function {
438 parameters: params,
439 body,
440 ..
441 } => {
442 let fn_type = FunctionType::try_from(typing(cont, self).value.clone()).unwrap();
443 let output_conversion = cont.get_type_anotation(&fn_type.get_return_type());
444
445 let list_of_types = params
446 .iter()
447 .map(ArgumentType::get_type)
448 .collect::<Vec<_>>();
449 let sub_context = params
450 .iter()
451 .map(|arg_typ| arg_typ.clone().to_var(cont))
452 .zip(list_of_types.clone())
453 .fold(cont.clone(), |context: Context, (var, typ)| {
454 context.clone().push_var_type(var, typ, &context)
455 });
456 let res = if output_conversion.is_empty() {
457 "".to_string()
458 } else {
459 " |> ".to_owned() + &output_conversion
460 };
461 (
462 format!(
463 "(function({}) {}{}) |> {}",
464 params
465 .iter()
466 .map(|x| x.to_r())
467 .collect::<Vec<_>>()
468 .join(", "),
469 body.to_r(&sub_context).0,
470 res,
471 cont.get_type_anotation(&fn_type.into())
472 ),
473 cont.clone(),
474 )
475 }
476 Lang::Variable { .. } => {
477 let var = Var::from_language(self.clone()).unwrap();
479 let name = if var.contains("__") {
480 var.replace("__", ".").get_name()
481 } else {
482 var.display_type(cont).get_name()
483 };
484 (name.to_string(), cont.clone())
485 }
486 Lang::FunctionApp {
487 identifier: exp,
488 arguments: vals,
489 ..
490 } => {
491 let var = Var::try_from(exp.clone()).unwrap();
492
493 let (exp_str, cont1) = exp.to_r(cont);
494 let fn_t = FunctionType::try_from(
495 cont1
496 .get_type_from_variable(&var)
497 .unwrap_or_else(|_| panic!("variable {} don't have a related type", var)),
498 )
499 .map(|ft| ft.adjust_nb_parameters(vals.len()))
500 .unwrap();
501 let new_args = fn_t
502 .get_param_types()
503 .iter()
504 .map(|arg| reduce_type(&cont1, arg))
505 .collect::<Vec<_>>();
506 let new_vals = vals
507 .iter()
508 .zip(new_args.iter())
509 .map(set_related_type_if_variable)
510 .collect::<Vec<_>>();
511 let (args, current_cont) = Translatable::from(cont1).join(&new_vals, ", ").into();
512 Var::from_language(*exp.clone())
513 .map(|var| {
514 let name = var.get_name();
515 let new_name = if &name[0..1] == "%" {
516 format!("`{}`", name.replace("__", "."))
517 } else {
518 name.replace("__", ".")
519 };
520 (format!("{}({})", new_name, args), current_cont.clone())
521 })
522 .unwrap_or((format!("{}({})", exp_str, args), current_cont))
523 }
524 Lang::VecFunctionApp {
525 identifier: exp,
526 arguments: vals,
527 ..
528 } => {
529 let var = Var::try_from(exp.clone()).unwrap();
530 let name = var.get_name();
531 let str_vals = vals
532 .iter()
533 .map(|x| x.to_r(cont).0)
534 .collect::<Vec<_>>()
535 .join(", ");
536 if name == "reduce" {
537 (format!("vec_reduce({})", str_vals), cont.clone())
538 } else if name == "extend" {
539 (format!("vec_extend({})", str_vals), cont.clone())
540 } else if cont.is_an_untyped_function(&name) {
541 let name = name.replace("__", ".");
542 let new_name = if &name[0..1] == "%" {
543 format!("`{}`", name)
544 } else {
545 name.to_string()
546 };
547 let s = format!("vec_apply({}, {})", new_name, str_vals);
548 (s, cont.clone())
549 } else {
550 let (exp_str, cont1) = exp.to_r(cont);
551 let fn_t =
552 FunctionType::try_from(cont1.get_type_from_variable(&var).unwrap_or_else(
553 |_| panic!("variable {} don't have a related type", var),
554 ))
555 .unwrap();
556 let new_args = fn_t
557 .get_param_types()
558 .iter()
559 .map(|arg| reduce_type(&cont1, arg))
560 .collect::<Vec<_>>();
561 let new_vals = vals
562 .iter()
563 .zip(new_args.iter())
564 .map(set_related_type_if_variable)
565 .collect::<Vec<_>>();
566 let (args, current_cont) =
567 Translatable::from(cont1).join(&new_vals, ", ").into();
568 Var::from_language(*exp.clone())
569 .map(|var| {
570 let name = var.get_name();
571 let new_name = if &name[0..1] == "%" {
572 format!("`{}`", name.replace("__", "."))
573 } else {
574 name.replace("__", ".")
575 };
576 (
577 format!("vec_apply({}, {})", new_name, args),
578 current_cont.clone(),
579 )
580 })
581 .unwrap_or((format!("vec_apply({}, {})", exp_str, args), current_cont))
582 }
583 }
584 Lang::ArrayIndexing {
585 identifier: exp,
586 indexing: val,
587 ..
588 } => {
589 let (exp_str, _) = exp.to_r(cont);
590 let (val_str, _) = val.to_simple_r(cont);
591 let (typ, _, _) = typing(cont, exp).to_tuple();
592 let res = match typ {
593 Type::Vec(_, _, _, _) => format!("{}[[{}]]", exp_str, val_str),
594 _ => "".to_string(),
595 };
596 (res, cont.clone())
597 }
598 Lang::GenFunc { name: func, .. } => (
599 format!("function(x, ...) UseMethod('{}')", func),
600 cont.clone(),
601 ),
602 Lang::Let {
603 variable: expr,
604 r#type: ttype,
605 expression: body,
606 is_public: _,
607 help_data: _,
608 } => {
609 let (body_str, new_cont) = body.to_r(cont);
610 let new_name = format_backtick(expr.clone().to_r(cont).0);
611
612 let (r_code, _new_name2) = Function::try_from((**body).clone())
613 .map(|_| {
614 let related_type = Var::try_from(expr)
615 .ok()
616 .map(|v| v.get_type())
617 .filter(|t| !matches!(t, Type::Empty(_) | Type::UnknownFunction(_)))
618 .unwrap_or_else(|| typing(cont, expr).value);
619 let method = match cont.get_environment() {
620 Environment::Project => format!(
621 "#' @method {}\n",
622 new_name.replace(".", " ").replace("`", "")
623 ),
624 _ => "".to_string(),
625 };
626 match related_type {
627 Type::Empty(_) => {
628 (format!("{} <- {}", new_name, body_str), new_name.clone())
629 }
630 Type::Any(_) | Type::Generic(_, _) => (
631 format!("{}.default <- {}", new_name, body_str),
632 new_name.clone(),
633 ),
634 _ => (
635 format!("{}{} <- {}", method, new_name, body_str),
636 new_name.clone(),
637 ),
638 }
639 })
640 .unwrap_or((format!("{} <- {}", new_name, body_str), new_name));
641 let code = if !ttype.is_empty() {
642 let type_annotation = new_cont.get_type_anotation(ttype);
643 format!("{} |> {}\n", r_code, type_annotation)
644 } else {
645 r_code + "\n"
646 };
647 (code, new_cont)
648 }
649 Lang::Array { .. } => {
650 let typ = self.typing(cont).value;
651
652 let dimension = ArrayType::try_from(typ.clone())
653 .unwrap()
654 .get_shape()
655 .map(|sha| format!("c({})", sha))
656 .unwrap_or_else(|| "c(0)".to_string());
657
658 let array = &self
659 .linearize_array()
660 .iter()
661 .map(|lang| lang.to_r(cont).0)
662 .collect::<Vec<_>>()
663 .join(", ")
664 .and_if(|lin_array| !lin_array.is_empty())
665 .map(|lin_array| format!("typed_vec({}, dim = {})", lin_array, dimension))
666 .unwrap_or("logical(0)".to_string());
667
668 (
669 format!("{} |> {}", array, cont.get_type_anotation(&typ)),
670 cont.to_owned(),
671 )
672 }
673 Lang::List { value: args, .. } => {
674 let (body, current_cont) = Translatable::from(cont.clone())
675 .join_arg_val(args, ",\n ")
676 .into();
677 let (typ, _, _) = typing(cont, self).to_tuple();
678 if let Type::Alias(alias_name, _, _, _) = &typ {
680 let is_record = cont
681 .aliases()
682 .find(|(var, _)| var.get_name() == *alias_name)
683 .map(|(_, t)| matches!(t, Type::Record(_, _)))
684 .unwrap_or(false);
685 if is_record {
686 return (format!("{}({})", alias_name, body), current_cont);
687 }
688 }
689 let anotation = cont.get_type_anotation(&typ);
690 cont.get_classes(&typ)
691 .map(|_| format!("list({}) |> {}", body, anotation))
692 .unwrap_or(format!("list({}) |> {}", body, anotation))
693 .to_some()
694 .map(|s| (s, current_cont))
695 .unwrap()
696 }
697 Lang::DataFrame { value: args, .. } => {
698 let (body, current_cont) = Translatable::from(cont.clone())
699 .join_arg_val(args, ",\n ")
700 .into();
701 let (typ, _, _) = typing(cont, self).to_tuple();
702 let anotation = cont.get_type_anotation(&typ);
703 cont.get_classes(&typ)
704 .map(|_| format!("data.frame({}) |> {}", body, anotation))
705 .unwrap_or(format!("data.frame({}) |> {}", body, anotation))
706 .to_some()
707 .map(|s| (s, current_cont))
708 .unwrap()
709 }
710 Lang::If {
711 condition: cond,
712 if_block: exp,
713 else_block: els,
714 ..
715 } if els == &Box::new(Lang::Empty(HelpData::default())) => {
716 Translatable::from(cont.clone())
717 .add("if(")
718 .to_r(cond)
719 .add(") {\n")
720 .to_r(exp)
721 .add(" \n}")
722 .into()
723 }
724 Lang::If {
725 condition: cond,
726 if_block: exp,
727 else_block: els,
728 help_data: _,
729 } => Translatable::from(cont.clone())
730 .add("if(")
731 .to_r(cond)
732 .add(") {\n")
733 .to_r(exp)
734 .add(" \n} else ")
735 .to_r(els)
736 .into(),
737 Lang::Tuple { value: vals, .. } => Translatable::from(cont.clone())
738 .add("struct(list(")
739 .join(vals, ", ")
740 .add("), 'Tuple')")
741 .into(),
742 Lang::Assign {
743 identifier: var,
744 expression: exp,
745 ..
746 } => Translatable::from(cont.clone())
747 .to_r(var)
748 .add(" <- ")
749 .to_r(exp)
750 .into(),
751 Lang::Comment { value: txt, .. } => ("#".to_string() + &txt, cont.clone()),
752 Lang::Tag {
753 name: s, value: t, ..
754 } => {
755 let (t_str, new_cont) = t.to_r(cont);
756 let (typ, _, _) = typing(cont, self).to_tuple();
757 let anotation = cont.get_type_anotation(&typ);
758 (
759 format!(
760 "structure(list('{}', body = {}), class = c('.{}', 'Tag')) |> {}",
761 s, t_str, s, anotation
762 ),
763 new_cont,
764 )
765 }
766 Lang::Null(_) => ("NULL".to_string(), cont.clone()),
767 Lang::Empty(_) => ("NA".to_string(), cont.clone()),
768 Lang::Lines { value: exps, .. } => {
769 Translatable::from(cont.clone()).join(exps, "\n").into()
770 }
771 Lang::Return { value: exp, .. } => Translatable::from(cont.clone())
772 .add("return ")
773 .to_r(exp)
774 .into(),
775 Lang::Lambda {
776 parameters: params,
777 body: bloc,
778 ..
779 } => {
780 let param_names: Vec<String> = params
781 .iter()
782 .map(|p: &Lang| match p {
783 Lang::Variable { name, .. } => name.clone(),
784 _ => "x".to_string(),
785 })
786 .collect();
787 (
788 format!(
789 "function({}) {{ {} }}",
790 param_names.join(", "),
791 bloc.to_r(cont).0
792 ),
793 cont.clone(),
794 )
795 }
796 Lang::VecBlock { value: bloc, .. } => (bloc.to_string(), cont.clone()),
797 Lang::Library { value: name, .. } => (format!("library({})", name), cont.clone()),
798 Lang::Match {
799 target: exp,
800 branches,
801 ..
802 } => (
803 to_pattern_match_statement((**exp).clone(), branches, cont),
804 cont.clone(),
805 ),
806 Lang::Exp { value: exp, .. } => (exp.clone(), cont.clone()),
807 Lang::ForLoop {
808 identifier: var,
809 expression: iterator,
810 body,
811 ..
812 } => Translatable::from(cont.clone())
813 .add("for (")
814 .to_r_safe(var)
815 .add(" in ")
816 .to_r_safe(iterator)
817 .add(") {\n")
818 .to_r_safe(body)
819 .add("\n}")
820 .into(),
821 Lang::RFunction {
822 parameters: vars,
823 body,
824 ..
825 } => Translatable::from(cont.clone())
826 .add("function (")
827 .join(vars, ", ")
828 .add(") \n")
829 .add(body)
830 .add("\n")
831 .into(),
832 Lang::Signature { .. } => ("".to_string(), cont.clone()),
833 Lang::Alias {
834 identifier: ident,
835 target_type: typ,
836 ..
837 } => {
838 let name = Var::from_language(*ident.clone())
839 .map(|v| v.get_name())
840 .unwrap_or_default();
841 match typ {
842 Type::Record(fields, _) => {
843 let mut sorted_fields: Vec<&ArgumentType> = fields.iter().collect();
844 sorted_fields.sort_by_key(|f| f.get_argument_str());
845 let params = sorted_fields
846 .iter()
847 .map(|f| f.get_argument_str())
848 .collect::<Vec<_>>()
849 .join(", ");
850 let field_args = sorted_fields
851 .iter()
852 .map(|f| {
853 let n = f.get_argument_str();
854 format!("{n} = {n}")
855 })
856 .collect::<Vec<_>>()
857 .join(", ");
858 (
859 format!(
860 "{name} <- function({params}) {{\n structure(list({field_args}), class = c(\"{name}\", \"list\"))\n}}"
861 ),
862 cont.clone(),
863 )
864 }
865 Type::Operator(_, _, _, _) => {
866 let union_name = &name;
868 let members = flatten_operator_union(typ);
869 let mut members_vec: Vec<Type> = members.into_iter().collect();
871 members_vec.sort_by_key(|t| t.pretty2());
872 let constructors: Vec<String> = members_vec
873 .iter()
874 .filter_map(|member| match member {
875 Type::Tag(variant_name, inner, _) => {
876 match inner.as_ref() {
877 Type::Empty(_) => Some(format!(
878 "{variant_name} <- function() {{\n structure(list(), class = c(\"{variant_name}\", \"{union_name}\", \"list\"))\n}}"
879 )),
880 _ => {
881 Some(format!(
883 "{variant_name} <- function(x) {{\n structure(list(x), class = c(\"{variant_name}\", \"{union_name}\", \"list\"))\n}}"
884 ))
885 }
886 }
887 }
888 Type::Alias(alias_name, _, _, _) => {
889 let record_fields = cont
891 .aliases()
892 .find(|(var, _)| var.get_name() == *alias_name)
893 .and_then(|(_, t)| {
894 if let Type::Record(fields, _) = t {
895 Some(fields.clone())
896 } else {
897 None
898 }
899 });
900 if let Some(fields) = record_fields {
901 let mut sorted: Vec<&ArgumentType> =
902 fields.iter().collect();
903 sorted.sort_by_key(|f| f.get_argument_str());
904 let params = sorted
905 .iter()
906 .map(|f| f.get_argument_str())
907 .collect::<Vec<_>>()
908 .join(", ");
909 let field_args = sorted
910 .iter()
911 .map(|f| {
912 let n = f.get_argument_str();
913 format!("{n} = {n}")
914 })
915 .collect::<Vec<_>>()
916 .join(", ");
917 Some(format!(
918 "{alias_name} <- function({params}) {{\n structure(list({field_args}), class = c(\"{alias_name}\", \"{union_name}\", \"list\"))\n}}"
919 ))
920 } else {
921 None
922 }
923 }
924 _ => None,
925 })
926 .collect();
927 (constructors.join("\n"), cont.clone())
928 }
929 _ => ("".to_string(), cont.clone()),
930 }
931 }
932 Lang::UnionConstructor {
933 variant_name,
934 fields,
935 ..
936 } => {
937 if fields.is_empty() {
938 (format!("{}()", variant_name), cont.clone())
939 } else {
940 let (body, current_cont) = Translatable::from(cont.clone())
941 .join_arg_val(fields, ", ")
942 .into();
943 (format!("{}({})", variant_name, body), current_cont)
944 }
945 }
946 Lang::KeyValue {
947 key: k, value: v, ..
948 } => (format!("{} = {}", k, v.to_r(cont).0), cont.clone()),
949 Lang::Vector { value: vals, .. } => {
950 let res = "c(".to_string()
951 + &vals
952 .iter()
953 .map(|x: &Lang| x.to_r(cont).0)
954 .collect::<Vec<_>>()
955 .join(", ")
956 + ")";
957 (res, cont.to_owned())
958 }
959 Lang::Not { value: exp, .. } => (format!("!{}", exp.to_r(cont).0), cont.clone()),
960 Lang::Sequence { body: vals, .. } => {
961 let res = if !vals.is_empty() {
962 "c(".to_string()
963 + &vals
964 .iter()
965 .map(|x: &Lang| "list(".to_string() + &x.to_r(cont).0 + ")")
966 .collect::<Vec<_>>()
967 .join(", ")
968 + ")"
969 } else {
970 "c(list())".to_string()
971 };
972 (res, cont.to_owned())
973 }
974 Lang::TestBlock {
975 value: body,
976 help_data: h,
977 } => {
978 let file_name = h
979 .get_file_data()
980 .map(|(name, _)| format!("test-{}", name))
981 .unwrap_or_else(|| "test-unknown".to_string())
982 .replace("TypR/", "")
983 .replace(".ty", ".R");
984
985 let file_path = format!("tests/testthat/{}", file_name);
986 let content = body.to_r(cont).0;
987
988 let _ = write_output_file(&file_path, &content);
989 ("".to_string(), cont.clone())
990 }
991 Lang::JSBlock(exp, _id, _h) => {
992 let js_cont = Context::default(); let res = exp.to_js(&js_cont).0;
994 (format!("'{}{}'", JS_HEADER, res), cont.clone())
995 }
996 Lang::WhileLoop {
997 condition, body, ..
998 } => (
999 format!(
1000 "while ({}) {{\n{}\n}}",
1001 condition.to_r(cont).0,
1002 body.to_r(cont).0
1003 ),
1004 cont.clone(),
1005 ),
1006 Lang::Break(_) => ("break".to_string(), cont.clone()),
1007 Lang::NA(_) => ("NA".to_string(), cont.clone()),
1008 Lang::Module {
1009 name,
1010 body,
1011 module_position: position,
1012 config,
1013 ..
1014 } => {
1015 let name_str = if (name == "main") && (config.environment == Environment::Project) {
1016 "a_main"
1017 } else {
1018 name
1019 };
1020
1021 let body_content = body
1022 .iter()
1023 .map(|lang| lang.to_r(cont).0)
1024 .collect::<Vec<_>>()
1025 .join("\n");
1026
1027 let mut exports: Vec<String> = Vec::new();
1029 let mut generics: Vec<String> = Vec::new();
1030 let mut generic_exports: Vec<String> = Vec::new();
1031
1032 for lang in body.iter() {
1033 if let Lang::Let {
1034 variable: var,
1035 is_public: true,
1036 ..
1037 } = lang
1038 {
1039 if let Some(v) = Var::from_language(*var.clone()) {
1040 let raw_name = v.get_name();
1041 let typed_name = v.clone().display_type(cont).get_name();
1042
1043 exports.push(format!(
1045 "{}${} <- {}",
1046 name_str, typed_name, typed_name
1047 ));
1048
1049 let var_type = v.get_type();
1051 if !var_type.is_empty() && typed_name != raw_name {
1052 let class_name = cont.get_class_unquoted(&var_type);
1053 exports.push(format!(
1054 "registerS3method(\"{}\", \"{}\", {})",
1055 raw_name, class_name, typed_name
1056 ));
1057
1058 let generic_def = format!(
1059 "{} <- function(x, ...) UseMethod(\"{}\")",
1060 raw_name, raw_name
1061 );
1062 if !generics.contains(&generic_def) {
1063 generics.push(generic_def);
1064 generic_exports.push(format!(
1065 "{}${} <- {}",
1066 name_str, raw_name, raw_name
1067 ));
1068 }
1069 }
1070 }
1071 }
1072 if let Lang::Alias {
1074 identifier: var,
1075 is_public: true,
1076 ..
1077 } = lang
1078 {
1079 if let Some(v) = Var::from_language(*var.clone()) {
1080 let alias_name = v.get_name();
1081 exports.push(format!(
1082 "{}${} <- {}",
1083 name_str, alias_name, alias_name
1084 ));
1085 }
1086 }
1087 }
1088
1089 let exports_str = if exports.is_empty() {
1090 String::new()
1091 } else {
1092 "\n".to_string() + &exports.join("\n")
1093 };
1094
1095 let generics_defs_str = if generics.is_empty() {
1096 String::new()
1097 } else {
1098 generics.join("\n") + "\n"
1099 };
1100
1101 let generic_exports_str = if generic_exports.is_empty() {
1102 String::new()
1103 } else {
1104 "\n".to_string() + &generic_exports.join("\n")
1105 };
1106
1107 let content = format!(
1108 "{}{} <- new.env(parent = emptyenv())\nlocal({{\n{}{}\n}}){}",
1109 generics_defs_str, name_str, body_content, exports_str, generic_exports_str
1110 );
1111
1112 match (position, config.environment) {
1113 (ModulePosition::Internal, _) => (content, cont.clone()),
1114 (ModulePosition::External, Environment::Wasm) => {
1116 let file_path = format!("{}.R", name_str);
1117 let _ = write_output_file(&file_path, &content);
1118 (content, cont.clone())
1119 }
1120 (ModulePosition::External, Environment::StandAlone)
1121 | (ModulePosition::External, Environment::Repl) => {
1122 let file_path = format!("{}.R", name_str);
1123 let _ = write_output_file(&file_path, &content);
1124 (format!("source('{}')", file_path), cont.clone())
1125 }
1126 (ModulePosition::External, Environment::Project) => {
1127 let file_path = format!("R/{}.R", name_str);
1128 let _ = write_output_file(&file_path, &content);
1129 ("".to_string(), cont.clone())
1130 }
1131 }
1132 }
1133 Lang::UseModule {
1134 module_path,
1135 selector,
1136 ..
1137 } => {
1138 use crate::components::language::use_lang::UseSelector;
1139
1140 let r_path = module_path.join("$");
1142
1143 let mod_type_opt = (|| {
1145 let root = cont
1146 .get_type_from_variable(&Var::from_name(&module_path[0]))
1147 .ok()?;
1148 let mut current = root;
1149 for seg in module_path.iter().skip(1) {
1150 current = current.to_module_type().ok()?.get_type_from_name(seg).ok()?;
1151 }
1152 current.to_module_type().ok()
1153 })();
1154
1155 let bindings: Vec<String> = match selector {
1156 UseSelector::Wildcard => mod_type_opt
1157 .map(|mt| {
1158 mt.get_public_members()
1159 .iter()
1160 .map(|m| {
1161 let name = m.get_argument_str();
1162 format!("{} <- {}${}", name, r_path, name)
1163 })
1164 .collect()
1165 })
1166 .unwrap_or_default(),
1167 UseSelector::Items(items) => items
1168 .iter()
1169 .map(|item| {
1170 let local_name = item.alias.as_deref().unwrap_or(&item.name);
1171 format!("{} <- {}${}", local_name, r_path, item.name)
1172 })
1173 .collect(),
1174 };
1175
1176 (bindings.join("\n"), cont.clone())
1177 }
1178 Lang::ModuleImport { .. } => ("".to_string(), cont.clone()),
1179 Lang::ConstructorCall {
1180 type_name,
1181 fields,
1182 ..
1183 } => {
1184 let (body, current_cont) = Translatable::from(cont.clone())
1185 .join_arg_val(fields, ", ")
1186 .into();
1187 (format!("{}({})", type_name, body), current_cont)
1188 }
1189 Lang::ArrayConstructorCall {
1190 type_name,
1191 elements,
1192 help_data: h,
1193 } => {
1194 let temp_array = Lang::Array {
1195 value: elements.clone(),
1196 help_data: h.clone(),
1197 };
1198 let typ = temp_array.typing(cont).value;
1199 let dimension = ArrayType::try_from(typ)
1200 .unwrap()
1201 .get_shape()
1202 .map(|sha| format!("c({})", sha))
1203 .unwrap_or_else(|| "c(0)".to_string());
1204 let lin_array = temp_array
1205 .linearize_array()
1206 .iter()
1207 .map(|lang| lang.to_r(cont).0)
1208 .collect::<Vec<_>>()
1209 .join(", ");
1210 let inner = if lin_array.is_empty() {
1211 "logical(0)".to_string()
1212 } else {
1213 format!("typed_vec({}, dim = {})", lin_array, dimension)
1214 };
1215 (format!("{}({})", type_name, inner), cont.clone())
1216 }
1217 _ => {
1218 println!("This language structure won't transpile: {:?}", self);
1219 ("".to_string(), cont.clone())
1220 }
1221 };
1222
1223 result
1224 }
1225}
1226
1227#[cfg(test)]
1228mod tests {
1229 use crate::utils::fluent_parser::FluentParser;
1230
1231 #[test]
1232 fn test_module_transpilation_with_pub() {
1233 let r_code = FluentParser::new()
1234 .push("module Math { let sq <- 2; @pub let pi <- 3; };")
1235 .run()
1236 .get_r_code()
1237 .iter()
1238 .cloned()
1239 .collect::<Vec<_>>()
1240 .join("\n");
1241 assert!(r_code.contains("Math <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1242 assert!(r_code.contains("local({"), "missing local block: {}", r_code);
1243 assert!(r_code.contains("Math$pi <- pi"), "missing public export: {}", r_code);
1244 assert!(!r_code.contains("Math$sq"), "private member should not be exported: {}", r_code);
1245 }
1246
1247 #[test]
1248 fn test_module_transpilation_no_pub() {
1249 let r_code = FluentParser::new()
1250 .push("module Empty { let x <- 1; };")
1251 .run()
1252 .get_r_code()
1253 .iter()
1254 .cloned()
1255 .collect::<Vec<_>>()
1256 .join("\n");
1257 assert!(r_code.contains("Empty <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1258 assert!(r_code.contains("local({"), "missing local block: {}", r_code);
1259 assert!(!r_code.contains("Empty$x"), "private member should not be exported: {}", r_code);
1260 }
1261
1262 #[test]
1263 fn test_module_s3_registration_for_typed_pub_fn() {
1264 let r_code = FluentParser::new()
1265 .push("module Math { @pub let double <- fn(x: Integer): Integer { x }; };")
1266 .run()
1267 .get_r_code()
1268 .iter()
1269 .cloned()
1270 .collect::<Vec<_>>()
1271 .join("\n");
1272 assert!(r_code.contains("Math <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1273 assert!(r_code.contains("registerS3method(\"double\", \"integer\", double.integer)"), "missing S3 registration: {}", r_code);
1274 assert!(r_code.contains("double <- function(x, ...) UseMethod(\"double\")"), "missing generic: {}", r_code);
1275 assert!(r_code.contains("Math$double <- double"), "missing generic export: {}", r_code);
1276 }
1277
1278 #[test]
1279 fn test_module_no_trailing_semicolon() {
1280 let r_code = FluentParser::new()
1281 .push("module Geo { @pub let pi <- 3; }")
1282 .run()
1283 .get_r_code()
1284 .iter()
1285 .cloned()
1286 .collect::<Vec<_>>()
1287 .join("\n");
1288 assert!(r_code.contains("Geo <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1289 assert!(r_code.contains("Geo$pi <- pi"), "missing public export: {}", r_code);
1290 }
1291
1292 #[test]
1293 fn test_import_module() {
1294 let r_code = FluentParser::new()
1295 .push("module Math { @pub let pi <- 3; }")
1296 .push("import Math")
1297 .run()
1298 .run()
1299 .get_r_code()
1300 .iter()
1301 .cloned()
1302 .collect::<Vec<_>>()
1303 .join("\n");
1304 assert!(r_code.contains("Math <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1305 }
1306
1307 #[test]
1308 fn test_import_module_as_alias() {
1309 let r_code = FluentParser::new()
1310 .push("module Math { @pub let pi <- 3; }")
1311 .push("import Math as Maths")
1312 .run()
1313 .run()
1314 .get_r_code()
1315 .iter()
1316 .cloned()
1317 .collect::<Vec<_>>()
1318 .join("\n");
1319 assert!(r_code.contains("Math <- new.env(parent = emptyenv())"), "missing env init: {}", r_code);
1320 assert!(r_code.contains("Maths <- Math") || r_code.contains("`Maths` <- Math"), "missing alias assignment: {}", r_code);
1321 }
1322
1323 #[test]
1324 fn test_use_items_transpiles() {
1325 let r_code = FluentParser::new()
1326 .push("module Math { @pub let pi <- 3; @pub let e <- 2; };")
1327 .push("use Math::{pi, e as euler};")
1328 .run()
1329 .run()
1330 .get_r_code()
1331 .iter()
1332 .cloned()
1333 .collect::<Vec<_>>()
1334 .join("\n");
1335 assert!(r_code.contains("pi <- Math$pi"), "missing pi binding: {}", r_code);
1336 assert!(r_code.contains("euler <- Math$e"), "missing euler binding: {}", r_code);
1337 }
1338
1339 #[test]
1340 fn test_use_wildcard_transpiles() {
1341 let r_code = FluentParser::new()
1342 .push("module Math { @pub let pi <- 3; @pub let e <- 2; let secret <- 0; };")
1343 .push("use Math::*;")
1344 .run()
1345 .run()
1346 .get_r_code()
1347 .iter()
1348 .cloned()
1349 .collect::<Vec<_>>()
1350 .join("\n");
1351 assert!(r_code.contains("pi <- Math$pi"), "missing pi binding: {}", r_code);
1352 assert!(r_code.contains("e <- Math$e"), "missing e binding: {}", r_code);
1353 assert!(!r_code.contains("secret <- Math$secret"), "private member must not be imported: {}", r_code);
1354 }
1355
1356 #[test]
1357 fn test_array_constructor_call_transpilation() {
1358 let r_code = FluentParser::new()
1359 .push("type Bits <- [Any, int];")
1360 .run()
1361 .push("let b <- Bits:[1, 2, 3];")
1362 .run()
1363 .get_r_code()
1364 .iter()
1365 .cloned()
1366 .collect::<Vec<_>>()
1367 .join("\n");
1368 assert!(r_code.contains("Bits(typed_vec("), "expected Bits(...) constructor: {}", r_code);
1369 assert!(r_code.contains("dim = c(3)"), "expected dimension annotation: {}", r_code);
1370 }
1371}