Skip to main content

typr_core/processes/transpiling/
mod.rs

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
35// Thread-local storage for generated files (used in WASM mode)
36thread_local! {
37    static GENERATED_FILES: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
38}
39
40/// Register a generated file (used for WASM mode to capture file outputs)
41pub 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
49/// Get all generated files
50pub fn get_generated_files() -> HashMap<String, String> {
51    GENERATED_FILES.with(|files| files.borrow().clone())
52}
53
54/// Clear all generated files
55pub fn clear_generated_files() {
56    GENERATED_FILES.with(|files| {
57        files.borrow_mut().clear();
58    });
59}
60
61/// Write a file - in native mode writes to filesystem, in WASM mode stores in memory
62#[cfg(not(feature = "wasm"))]
63fn write_output_file(path: &str, content: &str) -> Result<(), String> {
64    use std::fs;
65
66    // Also register in memory for consistency
67    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                // wildcard pattern: always matches
138                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
154/// Map a Type to its corresponding R type-check function name.
155fn 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        // Tag with a binding variable: .Some(a)
169        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        // Type pattern: x as int
183        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        // Tuple pattern: :{a, b, c}
194        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        // List/record pattern: :{nom: n, age: a}
221        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        // DataFrame pattern: data__frame(col1 = x, col2 = y)
251        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        // Wildcard: _
285        Lang::Variable { name, .. } if name == "_" => ("TRUE".to_string(), String::new()),
286        // Other variable: bind the whole value
287        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                //Here we only keep the variable name, the path and the type
478                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                // For record-alias types use the constructor directly
679                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                        // Union alias: generate constructors for each variant
867                        let union_name = &name;
868                        let members = flatten_operator_union(typ);
869                        // Sort for deterministic output
870                        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                                            // Tag with payload: wrap as single-field constructor
882                                            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                                    // Look up the record fields for this alias
890                                    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(); //TODO get js context from memory
993                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                // Build exports (inside local) and generics (outside local) for @pub members
1028                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                            // Export the (possibly type-suffixed) member into the module env
1044                            exports.push(format!(
1045                                "{}${} <- {}",
1046                                name_str, typed_name, typed_name
1047                            ));
1048
1049                            // For typed functions: register as S3 method and create generic
1050                            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                    // Export @pub opaque type constructors into the module environment
1073                    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                    // In WASM mode, inline all external modules instead of writing files
1115                    (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                // Build the R accessor prefix: A::B::C → A$B$C
1141                let r_path = module_path.join("$");
1142
1143                // Resolve the module type from context to enumerate public members for wildcards
1144                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}