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())
443 .expect("function expression should have a function type");
444 let return_type = fn_type.get_return_type();
445
446 let is_record_alias_return = match &return_type {
451 Type::Alias(alias_name, _, _, _) => cont
452 .aliases()
453 .find(|(var, _)| var.get_name() == *alias_name)
454 .map(|(_, t)| matches!(t, Type::Record(_, _)))
455 .unwrap_or(false),
456 _ => false,
457 };
458 let output_conversion = if is_record_alias_return {
459 "".to_string()
460 } else {
461 cont.get_type_anotation(&return_type)
462 };
463
464 let has_variadic = params.last().map(|p| p.is_variadic()).unwrap_or(false);
465 let list_of_types = params
466 .iter()
467 .map(ArgumentType::get_type)
468 .collect::<Vec<_>>();
469 let sub_context = params
470 .iter()
471 .map(|arg_typ| arg_typ.clone().to_var(cont))
472 .zip(list_of_types.clone())
473 .fold(cont.clone(), |context: Context, (var, typ)| {
474 context.clone().push_var_type(var, typ, &context)
475 });
476 let res = if output_conversion.is_empty() {
477 "".to_string()
478 } else {
479 " |> ".to_owned() + &output_conversion
480 };
481 let body_r = body.to_r(&sub_context).0;
482 let final_body_r = if has_variadic {
483 let vname = params.last().unwrap().get_argument_str();
484 if body_r.starts_with('{') {
486 format!("{{\n{} <- list(...){}", vname, &body_r[1..])
487 } else {
488 body_r
489 }
490 } else {
491 body_r
492 };
493 (
494 format!(
495 "(function({}) {}{}) |> {}",
496 params
497 .iter()
498 .map(|x| x.to_r())
499 .collect::<Vec<_>>()
500 .join(", "),
501 final_body_r,
502 res,
503 cont.get_type_anotation(&fn_type.into())
504 ),
505 cont.clone(),
506 )
507 }
508 Lang::Variable { .. } => {
509 let var = Var::from_language(self.clone()).unwrap();
511 let name = if var.contains("__") {
512 var.replace("__", ".").get_name()
513 } else {
514 var.display_type(cont).get_name()
515 };
516 (name.to_string(), cont.clone())
517 }
518 Lang::FunctionApp {
519 identifier: exp,
520 arguments: vals,
521 ..
522 } => {
523 let var = Var::try_from(exp.clone()).unwrap();
524
525 let (exp_str, cont1) = exp.to_r(cont);
526 let fn_t = FunctionType::try_from(
527 cont1
528 .get_type_from_variable(&var)
529 .unwrap_or_else(|_| panic!("variable {} don't have a related type", var)),
530 )
531 .map(|ft| ft.adjust_nb_parameters(vals.len()))
532 .expect("function application identifier should have a function type");
533 let new_args = fn_t
534 .get_param_types()
535 .iter()
536 .map(|arg| reduce_type(&cont1, arg))
537 .collect::<Vec<_>>();
538 let new_vals = vals
539 .iter()
540 .zip(new_args.iter())
541 .map(set_related_type_if_variable)
542 .collect::<Vec<_>>();
543 let (args, current_cont) = Translatable::from(cont1).join(&new_vals, ", ").into();
544 Var::from_language(*exp.clone())
545 .map(|var| {
546 let name = var.get_name();
547 let new_name = if &name[0..1] == "%" {
548 format!("`{}`", name.replace("__", "."))
549 } else {
550 name.replace("__", ".")
551 };
552 (format!("{}({})", new_name, args), current_cont.clone())
553 })
554 .unwrap_or((format!("{}({})", exp_str, args), current_cont))
555 }
556 Lang::VecFunctionApp {
557 identifier: exp,
558 arguments: vals,
559 ..
560 } => {
561 let var = Var::try_from(exp.clone()).unwrap();
562 let name = var.get_name();
563 let str_vals = vals
564 .iter()
565 .map(|x| x.to_r(cont).0)
566 .collect::<Vec<_>>()
567 .join(", ");
568 if name == "reduce" {
569 (format!("vec_reduce({})", str_vals), cont.clone())
570 } else if name == "extend" {
571 (format!("vec_extend({})", str_vals), cont.clone())
572 } else if cont.is_an_untyped_function(&name) {
573 let name = name.replace("__", ".");
574 let new_name = if &name[0..1] == "%" {
575 format!("`{}`", name)
576 } else {
577 name.to_string()
578 };
579 let s = format!("vec_apply({}, {})", new_name, str_vals);
580 (s, cont.clone())
581 } else {
582 let (exp_str, cont1) = exp.to_r(cont);
583 let fn_t = FunctionType::try_from(
584 cont1.get_type_from_variable(&var).unwrap_or_else(|_| {
585 panic!("variable {} don't have a related type", var)
586 }),
587 )
588 .expect("vector function application identifier should have a function type");
589 let new_args = fn_t
590 .get_param_types()
591 .iter()
592 .map(|arg| reduce_type(&cont1, arg))
593 .collect::<Vec<_>>();
594 let new_vals = vals
595 .iter()
596 .zip(new_args.iter())
597 .map(set_related_type_if_variable)
598 .collect::<Vec<_>>();
599 let (args, current_cont) =
600 Translatable::from(cont1).join(&new_vals, ", ").into();
601 Var::from_language(*exp.clone())
602 .map(|var| {
603 let name = var.get_name();
604 let new_name = if &name[0..1] == "%" {
605 format!("`{}`", name.replace("__", "."))
606 } else {
607 name.replace("__", ".")
608 };
609 (
610 format!("vec_apply({}, {})", new_name, args),
611 current_cont.clone(),
612 )
613 })
614 .unwrap_or((format!("vec_apply({}, {})", exp_str, args), current_cont))
615 }
616 }
617 Lang::ArrayIndexing {
618 identifier: exp,
619 indexing: val,
620 ..
621 } => {
622 let (exp_str, _) = exp.to_r(cont);
623 let (val_str, _) = val.to_simple_r(cont);
624 let (typ, _, _) = typing(cont, exp).to_tuple();
625 let res = match typ {
626 _ => format!("{}[[{}]]", exp_str, val_str),
627 };
628 (res, cont.clone())
629 }
630 Lang::GenFunc { name: func, .. } => (
631 format!("function(x, ...) UseMethod('{}')", func),
632 cont.clone(),
633 ),
634 Lang::Let {
635 variable: expr,
636 r#type: ttype,
637 expression: body,
638 is_public: _,
639 help_data: _,
640 } => {
641 let (body_str, new_cont) = body.to_r(cont);
642 let new_name = format_backtick(expr.clone().to_r(cont).0);
643
644 let (r_code, _new_name2) = Function::try_from((**body).clone())
645 .map(|_| {
646 let related_type = Var::try_from(expr)
647 .ok()
648 .map(|v| v.get_type())
649 .filter(|t| !matches!(t, Type::Empty(_) | Type::UnknownFunction(_)))
650 .unwrap_or_else(|| typing(cont, expr).value);
651 let method = match cont.get_environment() {
652 Environment::Project => format!(
653 "#' @method {}\n",
654 new_name.replace(".", " ").replace("`", "")
655 ),
656 _ => "".to_string(),
657 };
658 match related_type {
659 Type::Empty(_) => {
660 (format!("{} <- {}", new_name, body_str), new_name.clone())
661 }
662 Type::Any(_) | Type::Generic(_, _) => (
663 format!("{}.default <- {}", new_name, body_str),
664 new_name.clone(),
665 ),
666 _ => (
667 format!("{}{} <- {}", method, new_name, body_str),
668 new_name.clone(),
669 ),
670 }
671 })
672 .unwrap_or((format!("{} <- {}", new_name, body_str), new_name));
673 let code = if !ttype.is_empty() {
674 let type_annotation = new_cont.get_type_anotation(ttype);
675 format!("{} |> {}\n", r_code, type_annotation)
676 } else {
677 r_code + "\n"
678 };
679 (code, new_cont)
680 }
681 Lang::Array { .. } => {
682 let typ = self.typing(cont).value;
683
684 let dimension = ArrayType::try_from(typ.clone())
685 .expect("array literal should have an array type")
686 .get_shape()
687 .map(|sha| format!("c({})", sha))
688 .unwrap_or_else(|| "c(0)".to_string());
689
690 let array = &self
691 .linearize_array()
692 .iter()
693 .map(|lang| lang.to_r(cont).0)
694 .collect::<Vec<_>>()
695 .join(", ")
696 .and_if(|lin_array| !lin_array.is_empty())
697 .map(|lin_array| format!("typed_vec({}, dim = {})", lin_array, dimension))
698 .unwrap_or("logical(0)".to_string());
699
700 (
701 format!("{} |> {}", array, cont.get_type_anotation(&typ)),
702 cont.to_owned(),
703 )
704 }
705 Lang::List { value: args, .. } => {
706 let (body, current_cont) = Translatable::from(cont.clone())
707 .join_arg_val(args, ",\n ")
708 .into();
709 let (typ, _, _) = typing(cont, self).to_tuple();
710 if let Type::Alias(alias_name, _, _, _) = &typ {
712 let is_record = cont
713 .aliases()
714 .find(|(var, _)| var.get_name() == *alias_name)
715 .map(|(_, t)| matches!(t, Type::Record(_, _)))
716 .unwrap_or(false);
717 if is_record {
718 return (format!("{}({})", alias_name, body), current_cont);
719 }
720 }
721 let anotation = cont.get_type_anotation(&typ);
722 cont.get_classes(&typ)
723 .map(|_| format!("list({}) |> {}", body, anotation))
724 .unwrap_or(format!("list({}) |> {}", body, anotation))
725 .to_some()
726 .map(|s| (s, current_cont))
727 .unwrap()
728 }
729 Lang::DataFrame { value: args, .. } => {
730 let (body, current_cont) = Translatable::from(cont.clone())
731 .join_arg_val(args, ",\n ")
732 .into();
733 let (typ, _, _) = typing(cont, self).to_tuple();
734 let anotation = cont.get_type_anotation(&typ);
735 cont.get_classes(&typ)
736 .map(|_| format!("data.frame({}) |> {}", body, anotation))
737 .unwrap_or(format!("data.frame({}) |> {}", body, anotation))
738 .to_some()
739 .map(|s| (s, current_cont))
740 .unwrap()
741 }
742 Lang::If {
743 condition: cond,
744 if_block: exp,
745 else_block: els,
746 ..
747 } if els == &Box::new(Lang::Empty(HelpData::default())) => {
748 Translatable::from(cont.clone())
749 .add("if(")
750 .to_r(cond)
751 .add(") {\n")
752 .to_r(exp)
753 .add(" \n}")
754 .into()
755 }
756 Lang::If {
757 condition: cond,
758 if_block: exp,
759 else_block: els,
760 help_data: _,
761 } => Translatable::from(cont.clone())
762 .add("if(")
763 .to_r(cond)
764 .add(") {\n")
765 .to_r(exp)
766 .add(" \n} else ")
767 .to_r(els)
768 .into(),
769 Lang::Tuple { value: vals, .. } => Translatable::from(cont.clone())
770 .add("struct(list(")
771 .join(vals, ", ")
772 .add("), 'Tuple')")
773 .into(),
774 Lang::Assign {
775 identifier: var,
776 expression: exp,
777 ..
778 } => Translatable::from(cont.clone())
779 .to_r(var)
780 .add(" <- ")
781 .to_r(exp)
782 .into(),
783 Lang::Comment { value: txt, .. } => ("#".to_string() + &txt, cont.clone()),
784 Lang::Tag {
785 name: s, value: t, ..
786 } => {
787 let (t_str, new_cont) = t.to_r(cont);
788 let (typ, _, _) = typing(cont, self).to_tuple();
789 let anotation = cont.get_type_anotation(&typ);
790 (
791 format!(
792 "structure(list('{}', body = {}), class = c('.{}', 'Tag')) |> {}",
793 s, t_str, s, anotation
794 ),
795 new_cont,
796 )
797 }
798 Lang::Null(_) => ("NULL".to_string(), cont.clone()),
799 Lang::Empty(_) => ("NA".to_string(), cont.clone()),
800 Lang::Lines { value: exps, .. } => {
801 Translatable::from(cont.clone()).join(exps, "\n").into()
802 }
803 Lang::Return { value: exp, .. } => Translatable::from(cont.clone())
804 .add("return ")
805 .to_r(exp)
806 .into(),
807 Lang::Lambda {
808 parameters: params,
809 body: bloc,
810 ..
811 } => {
812 let param_names: Vec<String> = params
813 .iter()
814 .map(|p: &Lang| match p {
815 Lang::Variable { name, .. } => name.clone(),
816 _ => "x".to_string(),
817 })
818 .collect();
819 (
820 format!(
821 "function({}) {{ {} }}",
822 param_names.join(", "),
823 bloc.to_r(cont).0
824 ),
825 cont.clone(),
826 )
827 }
828 Lang::VecBlock { value: bloc, .. } => (bloc.to_string(), cont.clone()),
829 Lang::Library { value: name, .. } => (format!("library({})", name), cont.clone()),
830 Lang::Match {
831 target: exp,
832 branches,
833 ..
834 } => (
835 to_pattern_match_statement((**exp).clone(), branches, cont),
836 cont.clone(),
837 ),
838 Lang::Exp { value: exp, .. } => (exp.clone(), cont.clone()),
839 Lang::ForLoop {
840 identifier: var,
841 expression: iterator,
842 body,
843 ..
844 } => Translatable::from(cont.clone())
845 .add("for (")
846 .to_r_safe(var)
847 .add(" in ")
848 .to_r_safe(iterator)
849 .add(") {\n")
850 .to_r_safe(body)
851 .add("\n}")
852 .into(),
853 Lang::RFunction {
854 parameters: vars,
855 body,
856 ..
857 } => Translatable::from(cont.clone())
858 .add("function (")
859 .join(vars, ", ")
860 .add(") \n")
861 .add(body)
862 .add("\n")
863 .into(),
864 Lang::Signature { .. } => ("".to_string(), cont.clone()),
865 Lang::Alias {
866 identifier: ident,
867 target_type: typ,
868 ..
869 } => {
870 let name = Var::from_language(*ident.clone())
871 .map(|v| v.get_name())
872 .unwrap_or_default();
873 match typ {
874 Type::Record(fields, _) => {
875 let mut sorted_fields: Vec<&ArgumentType> = fields.iter().collect();
876 sorted_fields.sort_by_key(|f| f.get_argument_str());
877 let params = sorted_fields
878 .iter()
879 .map(|f| f.get_argument_str())
880 .collect::<Vec<_>>()
881 .join(", ");
882 let field_args = sorted_fields
883 .iter()
884 .map(|f| {
885 let n = f.get_argument_str();
886 format!("{n} = {n}")
887 })
888 .collect::<Vec<_>>()
889 .join(", ");
890 let constructor = format!(
891 "{name} <- function({params}) {{\n structure(list({field_args}), class = c(\"{name}\", \"list\"))\n}}"
892 );
893 let fields_quoted = sorted_fields
894 .iter()
895 .map(|f| format!("\"{}\"", f.get_argument_str()))
896 .collect::<Vec<_>>()
897 .join(", ");
898 let field_access = sorted_fields
899 .iter()
900 .map(|f| {
901 let n = f.get_argument_str();
902 format!("{n} = x[[\"{n}\"]]")
903 })
904 .collect::<Vec<_>>()
905 .join(", ");
906 let validator = format!(
907 ".validate_{name} <- function(x) {{\n required_fields <- c({fields_quoted})\n missing_fields <- setdiff(required_fields, names(x))\n if (length(missing_fields) > 0) {{\n stop(paste0(\"Validation failed for type {name}: missing fields: \", paste(missing_fields, collapse = \", \")))\n }}\n {name}({field_access})\n}}"
908 );
909 (
910 format!("{constructor}\n{validator}"),
911 cont.clone(),
912 )
913 }
914 Type::Operator(_, _, _, _) => {
915 let union_name = &name;
917 let members = flatten_operator_union(typ);
918 let mut members_vec: Vec<Type> = members.into_iter().collect();
920 members_vec.sort_by_key(|t| t.pretty2());
921 let constructors: Vec<String> = members_vec
922 .iter()
923 .filter_map(|member| match member {
924 Type::Tag(variant_name, inner, _) => {
925 match inner.as_ref() {
926 Type::Empty(_) => Some(format!(
927 "{variant_name} <- function() {{\n structure(list(), class = c(\"{variant_name}\", \"{union_name}\", \"list\"))\n}}"
928 )),
929 _ => {
930 Some(format!(
932 "{variant_name} <- function(x) {{\n structure(list(x), class = c(\"{variant_name}\", \"{union_name}\", \"list\"))\n}}"
933 ))
934 }
935 }
936 }
937 Type::Alias(alias_name, _, _, _) => {
938 let record_fields = cont
940 .aliases()
941 .find(|(var, _)| var.get_name() == *alias_name)
942 .and_then(|(_, t)| {
943 if let Type::Record(fields, _) = t {
944 Some(fields.clone())
945 } else {
946 None
947 }
948 });
949 if let Some(fields) = record_fields {
950 let mut sorted: Vec<&ArgumentType> =
951 fields.iter().collect();
952 sorted.sort_by_key(|f| f.get_argument_str());
953 let params = sorted
954 .iter()
955 .map(|f| f.get_argument_str())
956 .collect::<Vec<_>>()
957 .join(", ");
958 let field_args = sorted
959 .iter()
960 .map(|f| {
961 let n = f.get_argument_str();
962 format!("{n} = {n}")
963 })
964 .collect::<Vec<_>>()
965 .join(", ");
966 Some(format!(
967 "{alias_name} <- function({params}) {{\n structure(list({field_args}), class = c(\"{alias_name}\", \"{union_name}\", \"list\"))\n}}"
968 ))
969 } else {
970 None
971 }
972 }
973 _ => None,
974 })
975 .collect();
976 (constructors.join("\n"), cont.clone())
977 }
978 Type::Integer(tint, _) => {
979 use crate::components::r#type::tint::Tint;
980 let validator = match tint {
981 Tint::Val(i) => format!(
982 ".validate_{name} <- function(x) {{\n if (!is.integer(x)) stop(\"Validation failed for type {name}: expected int\")\n if (x != {i}L) stop(\"Validation failed for type {name}: expected literal {i}\")\n x\n}}"
983 ),
984 Tint::Unknown => format!(
985 ".validate_{name} <- function(x) {{\n if (!is.integer(x)) stop(\"Validation failed for type {name}: expected int\")\n x\n}}"
986 ),
987 };
988 (validator, cont.clone())
989 }
990 Type::Char(tchar, _) => {
991 use crate::components::r#type::tchar::Tchar;
992 let validator = match tchar {
993 Tchar::Val(s) => format!(
994 ".validate_{name} <- function(x) {{\n if (!is.character(x)) stop(\"Validation failed for type {name}: expected char\")\n if (x != '{s}') stop(\"Validation failed for type {name}: expected literal '{s}'\")\n x\n}}"
995 ),
996 Tchar::Unknown => format!(
997 ".validate_{name} <- function(x) {{\n if (!is.character(x)) stop(\"Validation failed for type {name}: expected char\")\n x\n}}"
998 ),
999 };
1000 (validator, cont.clone())
1001 }
1002 Type::Boolean(tbool, _) => {
1003 use crate::components::r#type::tbool::Tbool;
1004 let validator = match tbool {
1005 Tbool::Val(b) => {
1006 let r_val = if *b { "TRUE" } else { "FALSE" };
1007 format!(
1008 ".validate_{name} <- function(x) {{\n if (!is.logical(x)) stop(\"Validation failed for type {name}: expected bool\")\n if (x != {r_val}) stop(\"Validation failed for type {name}: expected literal {r_val}\")\n x\n}}"
1009 )
1010 }
1011 Tbool::Unknown => format!(
1012 ".validate_{name} <- function(x) {{\n if (!is.logical(x)) stop(\"Validation failed for type {name}: expected bool\")\n x\n}}"
1013 ),
1014 };
1015 (validator, cont.clone())
1016 }
1017 Type::Number(tnum, _) => {
1018 use crate::components::r#type::tnumber::Tnum;
1019 let validator = match tnum {
1020 Tnum::Val(v) => format!(
1021 ".validate_{name} <- function(x) {{\n if (!is.numeric(x)) stop(\"Validation failed for type {name}: expected num\")\n if (x != {v}) stop(\"Validation failed for type {name}: expected literal {v}\")\n x\n}}"
1022 ),
1023 Tnum::Unknown => format!(
1024 ".validate_{name} <- function(x) {{\n if (!is.numeric(x)) stop(\"Validation failed for type {name}: expected num\")\n x\n}}"
1025 ),
1026 };
1027 (validator, cont.clone())
1028 }
1029 Type::Tag(tag_name, inner_type, _) => {
1030 let body_validation = match inner_type.as_ref() {
1031 Type::Empty(_) => String::new(),
1032 Type::Integer(tint, _) => {
1033 use crate::components::r#type::tint::Tint;
1034 let null_check = format!("\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")\n if (!is.integer(x[[\"body\"]])) stop(\"Validation failed for type {name}: body must be int\")");
1035 match tint {
1036 Tint::Val(i) => format!("{null_check}\n if (x[[\"body\"]] != {i}L) stop(\"Validation failed for type {name}: body must be literal {i}\")"),
1037 Tint::Unknown => null_check,
1038 }
1039 }
1040 Type::Char(tchar, _) => {
1041 use crate::components::r#type::tchar::Tchar;
1042 let null_check = format!("\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")\n if (!is.character(x[[\"body\"]])) stop(\"Validation failed for type {name}: body must be char\")");
1043 match tchar {
1044 Tchar::Val(s) => format!("{null_check}\n if (x[[\"body\"]] != '{s}') stop(\"Validation failed for type {name}: body must be literal '{s}'\")"),
1045 Tchar::Unknown => null_check,
1046 }
1047 }
1048 Type::Boolean(tbool, _) => {
1049 use crate::components::r#type::tbool::Tbool;
1050 let null_check = format!("\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")\n if (!is.logical(x[[\"body\"]])) stop(\"Validation failed for type {name}: body must be bool\")");
1051 match tbool {
1052 Tbool::Val(b) => {
1053 let r_val = if *b { "TRUE" } else { "FALSE" };
1054 format!("{null_check}\n if (x[[\"body\"]] != {r_val}) stop(\"Validation failed for type {name}: body must be literal {r_val}\")")
1055 }
1056 Tbool::Unknown => null_check,
1057 }
1058 }
1059 Type::Number(tnum, _) => {
1060 use crate::components::r#type::tnumber::Tnum;
1061 let null_check = format!("\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")\n if (!is.numeric(x[[\"body\"]])) stop(\"Validation failed for type {name}: body must be num\")");
1062 match tnum {
1063 Tnum::Val(v) => format!("{null_check}\n if (x[[\"body\"]] != {v}) stop(\"Validation failed for type {name}: body must be literal {v}\")"),
1064 Tnum::Unknown => null_check,
1065 }
1066 }
1067 Type::Alias(alias_name, _, _, _) => format!(
1068 "\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")\n .validate_{alias_name}(x[[\"body\"]])"
1069 ),
1070 _ => format!(
1071 "\n if (is.null(x[[\"body\"]])) stop(\"Validation failed for type {name}: missing 'body' field\")"
1072 ),
1073 };
1074 let validator = format!(
1075 ".validate_{name} <- function(x) {{\n if (x[[1]] != '{tag_name}') stop(\"Validation failed for type {name}: expected tag '{tag_name}'\")\n{body_validation}\n x\n}}"
1076 );
1077 (validator, cont.clone())
1078 }
1079 _ => ("".to_string(), cont.clone()),
1080 }
1081 }
1082 Lang::UnionConstructor {
1083 variant_name,
1084 fields,
1085 ..
1086 } => {
1087 if fields.is_empty() {
1088 (format!("{}()", variant_name), cont.clone())
1089 } else {
1090 let (body, current_cont) = Translatable::from(cont.clone())
1091 .join_arg_val(fields, ", ")
1092 .into();
1093 (format!("{}({})", variant_name, body), current_cont)
1094 }
1095 }
1096 Lang::KeyValue {
1097 key: k, value: v, ..
1098 } => (format!("{} = {}", k, v.to_r(cont).0), cont.clone()),
1099 Lang::Vector { value: vals, .. } => {
1100 let res = "c(".to_string()
1101 + &vals
1102 .iter()
1103 .map(|x: &Lang| x.to_r(cont).0)
1104 .collect::<Vec<_>>()
1105 .join(", ")
1106 + ")";
1107 (res, cont.to_owned())
1108 }
1109 Lang::Not { value: exp, .. } => (format!("!{}", exp.to_r(cont).0), cont.clone()),
1110 Lang::Sequence { body: vals, .. } => {
1111 let res = if !vals.is_empty() {
1112 "c(".to_string()
1113 + &vals
1114 .iter()
1115 .map(|x: &Lang| "list(".to_string() + &x.to_r(cont).0 + ")")
1116 .collect::<Vec<_>>()
1117 .join(", ")
1118 + ")"
1119 } else {
1120 "c(list())".to_string()
1121 };
1122 (res, cont.to_owned())
1123 }
1124 Lang::TestBlock {
1125 value: body,
1126 help_data: h,
1127 } => {
1128 let file_name = h
1129 .get_file_data()
1130 .map(|(name, _)| format!("test-{}", name))
1131 .unwrap_or_else(|| "test-unknown".to_string())
1132 .replace("TypR/", "")
1133 .replace(".ty", ".R");
1134
1135 let file_path = format!("tests/testthat/{}", file_name);
1136 let content = body.to_r(cont).0;
1137
1138 let _ = write_output_file(&file_path, &content);
1139 ("".to_string(), cont.clone())
1140 }
1141 Lang::JSBlock(exp, _id, _h) => {
1142 let js_cont = Context::default(); let res = exp.to_js(&js_cont).0;
1144 (format!("'{}{}'", JS_HEADER, res), cont.clone())
1145 }
1146 Lang::WhileLoop {
1147 condition, body, ..
1148 } => (
1149 format!(
1150 "while ({}) {{\n{}\n}}",
1151 condition.to_r(cont).0,
1152 body.to_r(cont).0
1153 ),
1154 cont.clone(),
1155 ),
1156 Lang::Loop { body, .. } => (
1157 format!("while (TRUE) {{\n{}\n}}", body.to_r(cont).0),
1158 cont.clone(),
1159 ),
1160 Lang::Break(_) => ("break".to_string(), cont.clone()),
1161 Lang::NA(_) => ("NA".to_string(), cont.clone()),
1162 Lang::Module {
1163 name,
1164 body,
1165 module_position: position,
1166 config,
1167 ..
1168 } => {
1169 let name_str = if (name == "main") && (config.environment == Environment::Project) {
1170 "a_main"
1171 } else {
1172 name
1173 };
1174
1175 let body_content = body
1176 .iter()
1177 .map(|lang| lang.to_r(cont).0)
1178 .collect::<Vec<_>>()
1179 .join("\n");
1180
1181 let mut exports: Vec<String> = Vec::new();
1183 let mut generics: Vec<String> = Vec::new();
1184 let mut generic_exports: Vec<String> = Vec::new();
1185
1186 for lang in body.iter() {
1187 if let Lang::Let {
1188 variable: var,
1189 is_public: true,
1190 ..
1191 } = lang
1192 {
1193 if let Some(v) = Var::from_language(*var.clone()) {
1194 let raw_name = v.get_name();
1195 let typed_name = v.clone().display_type(cont).get_name();
1196
1197 exports.push(format!("{}${} <- {}", name_str, typed_name, typed_name));
1199
1200 let var_type = v.get_type();
1202 if !var_type.is_empty() && typed_name != raw_name {
1203 let class_name = cont.get_class_unquoted(&var_type);
1204 exports.push(format!(
1205 "registerS3method(\"{}\", \"{}\", {})",
1206 raw_name, class_name, typed_name
1207 ));
1208
1209 let generic_def = format!(
1210 "{} <- function(x, ...) UseMethod(\"{}\")",
1211 raw_name, raw_name
1212 );
1213 if !generics.contains(&generic_def) {
1214 generics.push(generic_def);
1215 generic_exports
1216 .push(format!("{}${} <- {}", name_str, raw_name, raw_name));
1217 }
1218 }
1219 }
1220 }
1221 if let Lang::Alias {
1223 identifier: var,
1224 is_public: true,
1225 ..
1226 } = lang
1227 {
1228 if let Some(v) = Var::from_language(*var.clone()) {
1229 let alias_name = v.get_name();
1230 exports.push(format!("{}${} <- {}", name_str, alias_name, alias_name));
1231 }
1232 }
1233 }
1234
1235 let exports_str = if exports.is_empty() {
1236 String::new()
1237 } else {
1238 "\n".to_string() + &exports.join("\n")
1239 };
1240
1241 let generics_defs_str = if generics.is_empty() {
1242 String::new()
1243 } else {
1244 generics.join("\n") + "\n"
1245 };
1246
1247 let generic_exports_str = if generic_exports.is_empty() {
1248 String::new()
1249 } else {
1250 "\n".to_string() + &generic_exports.join("\n")
1251 };
1252
1253 let content = format!(
1254 "{}{} <- new.env(parent = emptyenv())\nlocal({{\n{}{}\n}}){}",
1255 generics_defs_str, name_str, body_content, exports_str, generic_exports_str
1256 );
1257
1258 match (position, config.environment) {
1259 (ModulePosition::Internal, _) => (content, cont.clone()),
1260 (ModulePosition::External, Environment::Wasm) => {
1262 let file_path = format!("{}.R", name_str);
1263 let _ = write_output_file(&file_path, &content);
1264 (content, cont.clone())
1265 }
1266 (ModulePosition::External, Environment::StandAlone)
1267 | (ModulePosition::External, Environment::Repl) => {
1268 let file_path = format!("{}.R", name_str);
1269 let _ = write_output_file(&file_path, &content);
1270 (format!("source('{}')", file_path), cont.clone())
1271 }
1272 (ModulePosition::External, Environment::Project) => {
1273 let file_path = format!("R/{}.R", name_str);
1274 let _ = write_output_file(&file_path, &content);
1275 (format!("#' @include {}.R", name_str), cont.clone())
1276 }
1277 }
1278 }
1279 Lang::UseModule {
1280 module_path,
1281 selector,
1282 ..
1283 } => {
1284 use crate::components::language::use_lang::UseSelector;
1285
1286 let r_path = module_path.join("$");
1288
1289 let mod_type_opt = (|| {
1291 let root = cont
1292 .get_type_from_variable(&Var::from_name(&module_path[0]))
1293 .ok()?;
1294 let mut current = root;
1295 for seg in module_path.iter().skip(1) {
1296 current = current
1297 .to_module_type()
1298 .ok()?
1299 .get_type_from_name(seg)
1300 .ok()?;
1301 }
1302 current.to_module_type().ok()
1303 })();
1304
1305 let bindings: Vec<String> = match selector {
1306 UseSelector::Wildcard => mod_type_opt
1307 .map(|mt| {
1308 mt.get_public_members()
1309 .iter()
1310 .map(|m| {
1311 let name = m.get_argument_str();
1312 format!("{} <- {}${}", name, r_path, name)
1313 })
1314 .collect()
1315 })
1316 .unwrap_or_default(),
1317 UseSelector::Items(items) => items
1318 .iter()
1319 .map(|item| {
1320 let local_name = item.alias.as_deref().unwrap_or(&item.name);
1321 format!("{} <- {}${}", local_name, r_path, item.name)
1322 })
1323 .collect(),
1324 };
1325
1326 (bindings.join("\n"), cont.clone())
1327 }
1328 Lang::ModuleImport { .. } => ("".to_string(), cont.clone()),
1329 Lang::ConstructorCall {
1330 type_name, fields, ..
1331 } => {
1332 let (body, current_cont) = Translatable::from(cont.clone())
1333 .join_arg_val(fields, ", ")
1334 .into();
1335 (format!("{}({})", type_name, body), current_cont)
1336 }
1337 Lang::ArrayConstructorCall {
1338 type_name,
1339 elements,
1340 help_data: h,
1341 } => {
1342 let temp_array = Lang::Array {
1343 value: elements.clone(),
1344 help_data: h.clone(),
1345 };
1346 let typ = temp_array.typing(cont).value;
1347 let dimension = ArrayType::try_from(typ)
1348 .expect("array constructor call should have an array type")
1349 .get_shape()
1350 .map(|sha| format!("c({})", sha))
1351 .unwrap_or_else(|| "c(0)".to_string());
1352 let lin_array = temp_array
1353 .linearize_array()
1354 .iter()
1355 .map(|lang| lang.to_r(cont).0)
1356 .collect::<Vec<_>>()
1357 .join(", ");
1358 let inner = if lin_array.is_empty() {
1359 "logical(0)".to_string()
1360 } else {
1361 format!("typed_vec({}, dim = {})", lin_array, dimension)
1362 };
1363 (format!("{}({})", type_name, inner), cont.clone())
1364 }
1365 Lang::Import { .. } | Lang::Test { .. } | Lang::Use { .. } => {
1366 ("".to_string(), cont.clone())
1367 }
1368 Lang::ValidatingCast {
1369 expression,
1370 type_name,
1371 ..
1372 } => {
1373 let expr_r = expression.to_r(cont).0;
1374 (format!(".validate_{}({})", type_name, expr_r), cont.clone())
1375 }
1376 _ => ("".to_string(), cont.clone()),
1377 };
1378
1379 result
1380 }
1381}
1382
1383#[cfg(test)]
1384mod tests {
1385 use crate::utils::fluent_parser::FluentParser;
1386 use crate::components::language::{Lang, ModulePosition};
1387 use crate::components::context::config::{Config, Environment};
1388 use crate::components::context::Context;
1389 use crate::components::error_message::help_data::HelpData;
1390 use crate::processes::transpiling::translatable::RTranslatable;
1391
1392 #[test]
1393 fn test_validating_cast_transpiles_to_validate_call() {
1394 let r_code = FluentParser::new()
1395 .push("type Person <- list { name: char, age: int };")
1396 .run()
1397 .check_transpiling("x as! Person");
1398 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1399 assert!(
1400 r_str.contains(".validate_Person(x)"),
1401 "expected .validate_Person(x), got: {}",
1402 r_str
1403 );
1404 }
1405
1406 #[test]
1407 fn test_validating_cast_type_is_alias() {
1408 let typ = FluentParser::new()
1409 .push("type Person <- list { name: char, age: int };")
1410 .run()
1411 .check_typing("x as! Person");
1412 assert!(
1413 typ.pretty2().contains("Person"),
1414 "expected Alias(Person), got: {}",
1415 typ.pretty2()
1416 );
1417 }
1418
1419 #[test]
1420 fn test_alias_record_generates_validator() {
1421 let r_code = FluentParser::new()
1422 .check_transpiling("type Person <- list { name: char, age: int };");
1423 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1424 assert!(
1425 r_str.contains(".validate_Person <- function(x)"),
1426 "expected validator function, got: {}",
1427 r_str
1428 );
1429 assert!(
1430 r_str.contains("required_fields"),
1431 "expected field validation, got: {}",
1432 r_str
1433 );
1434 }
1435
1436 #[test]
1437 fn test_external_module_project_generates_include() {
1438 let module = Lang::Module {
1439 name: "MyModule".to_string(),
1440 body: vec![],
1441 module_position: ModulePosition::External,
1442 config: Config::default().set_environment(Environment::Project),
1443 help_data: HelpData::default(),
1444 };
1445 let context = Context::default().set_environment(Environment::Project);
1446 let (r_code, _) = module.to_r(&context);
1447 assert_eq!(r_code, "#' @include MyModule.R", "got: {}", r_code);
1448 }
1449
1450 #[test]
1451 fn test_module_transpilation_with_pub() {
1452 let r_code = FluentParser::new()
1453 .push("module Math { let sq <- 2; @pub let pi <- 3; };")
1454 .run()
1455 .get_r_code()
1456 .iter()
1457 .cloned()
1458 .collect::<Vec<_>>()
1459 .join("\n");
1460 assert!(
1461 r_code.contains("Math <- new.env(parent = emptyenv())"),
1462 "missing env init: {}",
1463 r_code
1464 );
1465 assert!(
1466 r_code.contains("local({"),
1467 "missing local block: {}",
1468 r_code
1469 );
1470 assert!(
1471 r_code.contains("Math$pi <- pi"),
1472 "missing public export: {}",
1473 r_code
1474 );
1475 assert!(
1476 !r_code.contains("Math$sq"),
1477 "private member should not be exported: {}",
1478 r_code
1479 );
1480 }
1481
1482 #[test]
1483 fn test_module_transpilation_no_pub() {
1484 let r_code = FluentParser::new()
1485 .push("module Empty { let x <- 1; };")
1486 .run()
1487 .get_r_code()
1488 .iter()
1489 .cloned()
1490 .collect::<Vec<_>>()
1491 .join("\n");
1492 assert!(
1493 r_code.contains("Empty <- new.env(parent = emptyenv())"),
1494 "missing env init: {}",
1495 r_code
1496 );
1497 assert!(
1498 r_code.contains("local({"),
1499 "missing local block: {}",
1500 r_code
1501 );
1502 assert!(
1503 !r_code.contains("Empty$x"),
1504 "private member should not be exported: {}",
1505 r_code
1506 );
1507 }
1508
1509 #[test]
1510 fn test_module_s3_registration_for_typed_pub_fn() {
1511 let r_code = FluentParser::new()
1512 .push("module Math { @pub let double <- fn(x: Integer): Integer { x }; };")
1513 .run()
1514 .get_r_code()
1515 .iter()
1516 .cloned()
1517 .collect::<Vec<_>>()
1518 .join("\n");
1519 assert!(
1520 r_code.contains("Math <- new.env(parent = emptyenv())"),
1521 "missing env init: {}",
1522 r_code
1523 );
1524 assert!(
1525 r_code.contains("registerS3method(\"double\", \"integer\", double.integer)"),
1526 "missing S3 registration: {}",
1527 r_code
1528 );
1529 assert!(
1530 r_code.contains("double <- function(x, ...) UseMethod(\"double\")"),
1531 "missing generic: {}",
1532 r_code
1533 );
1534 assert!(
1535 r_code.contains("Math$double <- double"),
1536 "missing generic export: {}",
1537 r_code
1538 );
1539 }
1540
1541 #[test]
1542 fn test_module_no_trailing_semicolon() {
1543 let r_code = FluentParser::new()
1544 .push("module Geo { @pub let pi <- 3; }")
1545 .run()
1546 .get_r_code()
1547 .iter()
1548 .cloned()
1549 .collect::<Vec<_>>()
1550 .join("\n");
1551 assert!(
1552 r_code.contains("Geo <- new.env(parent = emptyenv())"),
1553 "missing env init: {}",
1554 r_code
1555 );
1556 assert!(
1557 r_code.contains("Geo$pi <- pi"),
1558 "missing public export: {}",
1559 r_code
1560 );
1561 }
1562
1563 #[test]
1564 fn test_import_module() {
1565 let r_code = FluentParser::new()
1566 .push("module Math { @pub let pi <- 3; }")
1567 .push("import Math")
1568 .run()
1569 .run()
1570 .get_r_code()
1571 .iter()
1572 .cloned()
1573 .collect::<Vec<_>>()
1574 .join("\n");
1575 assert!(
1576 r_code.contains("Math <- new.env(parent = emptyenv())"),
1577 "missing env init: {}",
1578 r_code
1579 );
1580 }
1581
1582 #[test]
1583 fn test_import_module_as_alias() {
1584 let r_code = FluentParser::new()
1585 .push("module Math { @pub let pi <- 3; }")
1586 .push("import Math as Maths")
1587 .run()
1588 .run()
1589 .get_r_code()
1590 .iter()
1591 .cloned()
1592 .collect::<Vec<_>>()
1593 .join("\n");
1594 assert!(
1595 r_code.contains("Math <- new.env(parent = emptyenv())"),
1596 "missing env init: {}",
1597 r_code
1598 );
1599 assert!(
1600 r_code.contains("Maths <- Math") || r_code.contains("`Maths` <- Math"),
1601 "missing alias assignment: {}",
1602 r_code
1603 );
1604 }
1605
1606 #[test]
1607 fn test_use_items_transpiles() {
1608 let r_code = FluentParser::new()
1609 .push("module Math { @pub let pi <- 3; @pub let e <- 2; };")
1610 .push("use Math::{pi, e as euler};")
1611 .run()
1612 .run()
1613 .get_r_code()
1614 .iter()
1615 .cloned()
1616 .collect::<Vec<_>>()
1617 .join("\n");
1618 assert!(
1619 r_code.contains("pi <- Math$pi"),
1620 "missing pi binding: {}",
1621 r_code
1622 );
1623 assert!(
1624 r_code.contains("euler <- Math$e"),
1625 "missing euler binding: {}",
1626 r_code
1627 );
1628 }
1629
1630 #[test]
1631 fn test_use_wildcard_transpiles() {
1632 let r_code = FluentParser::new()
1633 .push("module Math { @pub let pi <- 3; @pub let e <- 2; let secret <- 0; };")
1634 .push("use Math::*;")
1635 .run()
1636 .run()
1637 .get_r_code()
1638 .iter()
1639 .cloned()
1640 .collect::<Vec<_>>()
1641 .join("\n");
1642 assert!(
1643 r_code.contains("pi <- Math$pi"),
1644 "missing pi binding: {}",
1645 r_code
1646 );
1647 assert!(
1648 r_code.contains("e <- Math$e"),
1649 "missing e binding: {}",
1650 r_code
1651 );
1652 assert!(
1653 !r_code.contains("secret <- Math$secret"),
1654 "private member must not be imported: {}",
1655 r_code
1656 );
1657 }
1658
1659 #[test]
1660 fn test_array_constructor_call_transpilation() {
1661 let r_code = FluentParser::new()
1662 .push("type Bits <- [Any, int];")
1663 .run()
1664 .push("let b <- Bits:[1, 2, 3];")
1665 .run()
1666 .get_r_code()
1667 .iter()
1668 .cloned()
1669 .collect::<Vec<_>>()
1670 .join("\n");
1671 assert!(
1672 r_code.contains("Bits(typed_vec("),
1673 "expected Bits(...) constructor: {}",
1674 r_code
1675 );
1676 assert!(
1677 r_code.contains("dim = c(3)"),
1678 "expected dimension annotation: {}",
1679 r_code
1680 );
1681 }
1682
1683 #[test]
1684 fn test_record_alias_return_no_constructor_pipe() {
1685 let r_code = FluentParser::new()
1689 .push("type Point <- list { x: int, y: int };")
1690 .run()
1691 .push("let incr <- fn(p: Point): Point { Point:{x: (p$x+1), y: (p$y+1)} };")
1692 .run()
1693 .get_r_code()
1694 .iter()
1695 .cloned()
1696 .collect::<Vec<_>>()
1697 .join("\n");
1698 assert!(
1700 !r_code.contains("}) |> Point()"),
1701 "record alias output conversion should not be added: {}",
1702 r_code
1703 );
1704 assert!(
1706 r_code.contains("|> Generic()") || r_code.contains("|> Function"),
1707 "function type annotation should still be applied: {}",
1708 r_code
1709 );
1710 }
1711
1712 #[test]
1713 fn test_alias_int_generates_validator() {
1714 let r_code = FluentParser::new()
1715 .check_transpiling("type Meters <- int;");
1716 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1717 assert!(
1718 r_str.contains(".validate_Meters <- function(x)"),
1719 "expected validator function, got: {}",
1720 r_str
1721 );
1722 assert!(
1723 r_str.contains("is.integer"),
1724 "expected is.integer check, got: {}",
1725 r_str
1726 );
1727 }
1728
1729 #[test]
1730 fn test_alias_char_generates_validator() {
1731 let r_code = FluentParser::new()
1732 .check_transpiling("type Name <- char;");
1733 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1734 assert!(
1735 r_str.contains(".validate_Name <- function(x)"),
1736 "expected validator function, got: {}",
1737 r_str
1738 );
1739 assert!(
1740 r_str.contains("is.character"),
1741 "expected is.character check, got: {}",
1742 r_str
1743 );
1744 }
1745
1746 #[test]
1747 fn test_alias_bool_generates_validator() {
1748 let r_code = FluentParser::new()
1749 .check_transpiling("type Flag <- bool;");
1750 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1751 assert!(
1752 r_str.contains(".validate_Flag <- function(x)"),
1753 "expected validator function, got: {}",
1754 r_str
1755 );
1756 assert!(
1757 r_str.contains("is.logical"),
1758 "expected is.logical check, got: {}",
1759 r_str
1760 );
1761 }
1762
1763 #[test]
1764 fn test_alias_num_generates_validator() {
1765 let r_code = FluentParser::new()
1766 .check_transpiling("type Real <- num;");
1767 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1768 assert!(
1769 r_str.contains(".validate_Real <- function(x)"),
1770 "expected validator function, got: {}",
1771 r_str
1772 );
1773 assert!(
1774 r_str.contains("is.numeric"),
1775 "expected is.numeric check, got: {}",
1776 r_str
1777 );
1778 }
1779
1780 #[test]
1781 fn test_validating_cast_int() {
1782 let r_code = FluentParser::new()
1783 .push("type Meters <- int;")
1784 .run()
1785 .check_transpiling("x as! Meters");
1786 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1787 assert!(
1788 r_str.contains(".validate_Meters(x)"),
1789 "expected .validate_Meters(x), got: {}",
1790 r_str
1791 );
1792 }
1793
1794 #[test]
1795 fn test_tag_alias_char_generates_validator() {
1796 let r_code = FluentParser::new()
1797 .check_transpiling("type Hello <- .Hello(char);");
1798 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1799 assert!(
1800 r_str.contains(".validate_Hello <- function(x)"),
1801 "expected validator function, got: {}",
1802 r_str
1803 );
1804 assert!(
1805 r_str.contains("x[[1]] != 'Hello'"),
1806 "expected tag name check, got: {}",
1807 r_str
1808 );
1809 assert!(
1810 r_str.contains("x[[\"body\"]]"),
1811 "expected body field check, got: {}",
1812 r_str
1813 );
1814 assert!(
1815 r_str.contains("is.character"),
1816 "expected is.character check on body, got: {}",
1817 r_str
1818 );
1819 }
1820
1821 #[test]
1822 fn test_tag_alias_int_generates_validator() {
1823 let r_code = FluentParser::new()
1824 .check_transpiling("type Count <- .Count(int);");
1825 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1826 assert!(
1827 r_str.contains(".validate_Count <- function(x)"),
1828 "expected validator, got: {}",
1829 r_str
1830 );
1831 assert!(
1832 r_str.contains("x[[1]] != 'Count'"),
1833 "expected tag name check, got: {}",
1834 r_str
1835 );
1836 assert!(
1837 r_str.contains("is.integer"),
1838 "expected is.integer check on body, got: {}",
1839 r_str
1840 );
1841 }
1842
1843 #[test]
1844 fn test_tag_alias_with_alias_body_calls_nested_validator() {
1845 let r_code = FluentParser::new()
1846 .push("type Name <- char;")
1847 .run()
1848 .check_transpiling("type Tagged <- .Tagged(Name);");
1849 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1850 assert!(
1851 r_str.contains(".validate_Tagged <- function(x)"),
1852 "expected validator, got: {}",
1853 r_str
1854 );
1855 assert!(
1856 r_str.contains(".validate_Name(x[[\"body\"]])"),
1857 "expected nested validator call, got: {}",
1858 r_str
1859 );
1860 }
1861
1862 #[test]
1863 fn test_literal_char_alias_generates_exact_validator() {
1864 let r_code = FluentParser::new()
1865 .check_transpiling("type Hello <- \"hello\";");
1866 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1867 assert!(
1868 r_str.contains(".validate_Hello <- function(x)"),
1869 "expected validator function, got: {}",
1870 r_str
1871 );
1872 assert!(
1873 r_str.contains("x != 'hello'"),
1874 "expected literal equality check, got: {}",
1875 r_str
1876 );
1877 }
1878
1879 #[test]
1880 fn test_literal_int_alias_generates_exact_validator() {
1881 let r_code = FluentParser::new()
1882 .check_transpiling("type Byte <- 89;");
1883 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1884 assert!(
1885 r_str.contains(".validate_Byte <- function(x)"),
1886 "expected validator function, got: {}",
1887 r_str
1888 );
1889 assert!(
1890 r_str.contains("x != 89L"),
1891 "expected literal equality check, got: {}",
1892 r_str
1893 );
1894 }
1895
1896 #[test]
1897 fn test_literal_num_alias_generates_exact_validator() {
1898 let r_code = FluentParser::new()
1899 .check_transpiling("type Pi <- 3.14;");
1900 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1901 assert!(
1902 r_str.contains(".validate_Pi <- function(x)"),
1903 "expected validator function, got: {}",
1904 r_str
1905 );
1906 assert!(
1907 r_str.contains("x != 3.14"),
1908 "expected literal equality check, got: {}",
1909 r_str
1910 );
1911 }
1912
1913 #[test]
1914 fn test_literal_bool_true_alias_generates_exact_validator() {
1915 let r_code = FluentParser::new()
1916 .check_transpiling("type Yes <- true;");
1917 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1918 assert!(
1919 r_str.contains(".validate_Yes <- function(x)"),
1920 "expected validator function, got: {}",
1921 r_str
1922 );
1923 assert!(
1924 r_str.contains("x != TRUE"),
1925 "expected literal TRUE check, got: {}",
1926 r_str
1927 );
1928 }
1929
1930 #[test]
1931 fn test_literal_bool_false_alias_generates_exact_validator() {
1932 let r_code = FluentParser::new()
1933 .check_transpiling("type No <- false;");
1934 let r_str = r_code.iter().cloned().collect::<Vec<_>>().join("\n");
1935 assert!(
1936 r_str.contains(".validate_No <- function(x)"),
1937 "expected validator function, got: {}",
1938 r_str
1939 );
1940 assert!(
1941 r_str.contains("x != FALSE"),
1942 "expected literal FALSE check, got: {}",
1943 r_str
1944 );
1945 }
1946}