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::format_backtick;
7use crate::components::language::function_lang::Function;
8use crate::components::language::operators::Op;
9use crate::components::language::set_related_type_if_variable;
10use crate::components::language::var::Var;
11use crate::components::language::Lang;
12use crate::components::language::ModulePosition;
13use crate::components::r#type::array_type::ArrayType;
14use crate::components::r#type::function_type::FunctionType;
15use crate::components::r#type::Type;
16use crate::processes::transpiling::translatable::Translatable;
17use crate::processes::type_checking::type_comparison::reduce_type;
18use crate::processes::type_checking::typing;
19use translatable::RTranslatable;
20
21#[cfg(not(feature = "wasm"))]
22use std::fs::File;
23#[cfg(not(feature = "wasm"))]
24use std::io::Write;
25#[cfg(not(feature = "wasm"))]
26use std::path::PathBuf;
27
28use std::cell::RefCell;
29use std::collections::HashMap;
30
31thread_local! {
33 static GENERATED_FILES: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
34}
35
36pub fn register_generated_file(path: &str, content: &str) {
38 GENERATED_FILES.with(|files| {
39 files
40 .borrow_mut()
41 .insert(path.to_string(), content.to_string());
42 });
43}
44
45pub fn get_generated_files() -> HashMap<String, String> {
47 GENERATED_FILES.with(|files| files.borrow().clone())
48}
49
50pub fn clear_generated_files() {
52 GENERATED_FILES.with(|files| {
53 files.borrow_mut().clear();
54 });
55}
56
57#[cfg(not(feature = "wasm"))]
59fn write_output_file(path: &str, content: &str) -> Result<(), String> {
60 use std::fs;
61
62 register_generated_file(path, content);
64
65 let path_buf = PathBuf::from(path);
66 if let Some(parent) = path_buf.parent() {
67 fs::create_dir_all(parent).map_err(|e| e.to_string())?;
68 }
69 let mut file = File::create(&path_buf).map_err(|e| e.to_string())?;
70 file.write_all(content.as_bytes())
71 .map_err(|e| e.to_string())?;
72 Ok(())
73}
74
75#[cfg(feature = "wasm")]
76fn write_output_file(path: &str, content: &str) -> Result<(), String> {
77 register_generated_file(path, content);
78 Ok(())
79}
80
81pub trait ToSome {
82 fn to_some(self) -> Option<Self>
83 where
84 Self: Sized;
85}
86
87impl<T: Sized> ToSome for T {
88 fn to_some(self) -> Option<Self> {
89 Some(self)
90 }
91}
92
93trait AndIf {
94 fn and_if<F>(self, condition: F) -> Option<Self>
95 where
96 F: Fn(Self) -> bool,
97 Self: Sized;
98}
99
100impl<T: Clone> AndIf for T {
101 fn and_if<F>(self, condition: F) -> Option<Self>
102 where
103 F: Fn(Self) -> bool,
104 {
105 if condition(self.clone()) {
106 Some(self)
107 } else {
108 None
109 }
110 }
111}
112
113const JS_HEADER: &str = "";
114
115fn condition_to_if(var: &Var, typ: &Type, context: &Context) -> String {
116 format!(
117 "any(class({}) == c({}))",
118 var.get_name(),
119 context.get_class(typ)
120 )
121}
122
123fn to_if_statement(
124 var: Var,
125 exp: Lang,
126 branches: &[(Type, Box<Lang>)],
127 context: &Context,
128) -> String {
129 let res = branches
130 .iter()
131 .map(|(typ, body)| (condition_to_if(&var, typ, context), body))
132 .enumerate()
133 .map(|(id, (cond, body))| {
134 if id == 0 {
135 format!("if ({}) {{ \n {} \n }}", cond, body.to_r(context).0)
136 } else {
137 format!("else if ({}) {{ \n {} \n }}", cond, body.to_r(context).0)
138 }
139 })
140 .collect::<Vec<_>>()
141 .join(" ");
142 format!(
143 "{{\n {} <- {} \n {}\n}}",
144 var.get_name(),
145 exp.to_r(context).0,
146 res
147 )
148}
149
150impl RTranslatable<(String, Context)> for Lang {
151 fn to_r(&self, cont: &Context) -> (String, Context) {
152 let result = match self {
153 Lang::Bool(b, _) => {
154 let (typ, _, _) = typing(cont, self).to_tuple();
155 let anotation = cont.get_type_anotation(&typ);
156 (
157 format!("{} |> {}", b.to_string().to_uppercase(), anotation),
158 cont.clone(),
159 )
160 }
161 Lang::Number(n, _) => {
162 let (typ, _, _) = typing(cont, self).to_tuple();
163 let anotation = cont.get_type_anotation(&typ);
164 (format!("{} |> {}", n, anotation), cont.clone())
165 }
166 Lang::Integer(i, _) => {
167 let (typ, _, _) = typing(cont, self).to_tuple();
168 let anotation = cont.get_type_anotation(&typ);
169 (format!("{}L |> {}", i, anotation), cont.clone())
170 }
171 Lang::Char(s, _) => {
172 let (typ, _, _) = typing(cont, self).to_tuple();
173 let anotation = cont.get_type_anotation(&typ);
174 (format!("'{}' |> {}", s, anotation), cont.clone())
175 }
176 Lang::Operator(Op::Dot(_), e1, e2, _) | Lang::Operator(Op::Pipe(_), e1, e2, _) => {
177 let e1 = (**e1).clone();
178 let e2 = (**e2).clone();
179 match e2.clone() {
180 Lang::Variable(_, _, _, _) => Translatable::from(cont.clone())
181 .to_r(&e2)
182 .add("[['")
183 .to_r(&e1)
184 .add("']]")
185 .into(),
186 Lang::Record(fields, _) => {
187 let at = fields[0].clone();
188 Translatable::from(cont.clone())
189 .add("within(")
190 .to_r(&e2)
191 .add(", { ")
192 .add(&at.get_argument())
193 .add(" <- ")
194 .to_r(&at.get_value())
195 .add(" })")
196 .into()
197 }
198 Lang::FunctionApp(var, v, h) => {
199 let v = [e1].iter().chain(v.iter()).cloned().collect();
200 Lang::FunctionApp(var, v, h).to_r(cont)
201 }
202 _ => Translatable::from(cont.clone())
203 .to_r(&e2)
204 .add("[[")
205 .add("]]")
206 .to_r(&e1)
207 .into(),
208 }
209 }
210 Lang::Operator(Op::Dollar(_), e1, e2, _) => {
211 let e1 = (**e1).clone();
212 let e2 = (**e2).clone();
213 let t1 = typing(cont, &e1).value;
214 let val = match (t1.clone(), e2.clone()) {
215 (Type::Vec(vtype, _, _, _), Lang::Variable(name, _, _, _))
216 if vtype.is_array() =>
217 {
218 format!("vec_apply(get, {}, typed_vec('{}'))", e1.to_r(cont).0, name)
219 }
220 (_, Lang::Variable(name, _, _, _)) => format!("{}${}", e1.to_r(cont).0, name),
221 _ => format!("{}${}", e1.to_r(cont).0, e2.to_r(cont).0),
222 };
224 (val, cont.clone())
225 }
226 Lang::Operator(op, e1, e2, _) => {
227 let op_str = format!(" {} ", op.to_string());
228 Translatable::from(cont.clone())
229 .to_r(e1)
230 .add(&op_str)
231 .to_r(e2)
232 .into()
233 }
234 Lang::Scope(exps, _) => Translatable::from(cont.clone())
235 .add("{\n")
236 .join(exps, "\n")
237 .add("\n}")
238 .into(),
239 Lang::Function(args, _, body, _) => {
240 let fn_type = FunctionType::try_from(typing(cont, self).value.clone()).unwrap();
241 let output_conversion = cont.get_type_anotation(&fn_type.get_return_type());
242 let res = (output_conversion == "")
243 .then_some("".to_string())
244 .unwrap_or(" |> ".to_owned() + &output_conversion);
245 (
246 format!(
247 "(function({}) {}{}) |> {}",
248 args.iter().map(|x| x.to_r()).collect::<Vec<_>>().join(", "),
249 body.to_r(cont).0,
250 res,
251 cont.get_type_anotation(&fn_type.into())
252 ),
253 cont.clone(),
254 )
255 }
256 Lang::Variable(_, _, _, _) => {
257 let var = Var::from_language(self.clone()).unwrap();
259 let name = if var.contains("__") {
260 var.replace("__", ".").get_name()
261 } else {
262 var.display_type(cont).get_name()
263 };
264 ((&name).to_string(), cont.clone())
265 }
266 Lang::FunctionApp(exp, vals, _) => {
267 let var = Var::try_from(exp.clone()).unwrap();
268
269 let (exp_str, cont1) = exp.to_r(cont);
270 let fn_t = FunctionType::try_from(
271 cont1
272 .get_type_from_variable(&var)
273 .expect(&format!("variable {} don't have a related type", var)),
274 )
275 .map(|ft| ft.adjust_nb_parameters(vals.len()))
276 .unwrap();
277 let new_args = fn_t
278 .get_param_types()
279 .into_iter()
280 .map(|arg| reduce_type(&cont1, &arg))
281 .collect::<Vec<_>>();
282 let new_vals = vals
283 .into_iter()
284 .zip(new_args.iter())
285 .map(set_related_type_if_variable)
286 .collect::<Vec<_>>();
287 let (args, current_cont) = Translatable::from(cont1).join(&new_vals, ", ").into();
288 Var::from_language(*exp.clone())
289 .map(|var| {
290 let name = var.get_name();
291 let new_name = if &name[0..1] == "%" {
292 format!("`{}`", name.replace("__", "."))
293 } else {
294 name.replace("__", ".")
295 };
296 (format!("{}({})", new_name, args), current_cont.clone())
297 })
298 .unwrap_or((format!("{}({})", exp_str, args), current_cont))
299 }
300 Lang::VecFunctionApp(exp, vals, _) => {
301 let var = Var::try_from(exp.clone()).unwrap();
302 let name = var.get_name();
303 let str_vals = vals
304 .iter()
305 .map(|x| x.to_r(cont).0)
306 .collect::<Vec<_>>()
307 .join(", ");
308 if name == "reduce" {
309 (format!("vec_reduce({})", str_vals), cont.clone())
310 } else if cont.is_an_untyped_function(&name) {
311 let name = name.replace("__", ".");
312 let new_name = if &name[0..1] == "%" {
313 format!("`{}`", name)
314 } else {
315 name.to_string()
316 };
317 let s = format!("{}({})", new_name, str_vals);
318 (s, cont.clone())
319 } else {
320 let (exp_str, cont1) = exp.to_r(cont);
321 let fn_t = FunctionType::try_from(
322 cont1
323 .get_type_from_variable(&var)
324 .expect(&format!("variable {} don't have a related type", var)),
325 )
326 .unwrap();
327 let new_args = fn_t
328 .get_param_types()
329 .into_iter()
330 .map(|arg| reduce_type(&cont1, &arg))
331 .collect::<Vec<_>>();
332 let new_vals = vals
333 .into_iter()
334 .zip(new_args.iter())
335 .map(set_related_type_if_variable)
336 .collect::<Vec<_>>();
337 let (args, current_cont) =
338 Translatable::from(cont1).join(&new_vals, ", ").into();
339 Var::from_language(*exp.clone())
340 .map(|var| {
341 let name = var.get_name();
342 let new_name = if &name[0..1] == "%" {
343 format!("`{}`", name.replace("__", "."))
344 } else {
345 name.replace("__", ".")
346 };
347 (
348 format!("vec_apply({}, {})", new_name, args),
349 current_cont.clone(),
350 )
351 })
352 .unwrap_or((format!("vec_apply({}, {})", exp_str, args), current_cont))
353 }
354 }
355 Lang::ArrayIndexing(exp, val, _) => {
356 let (exp_str, _) = exp.to_r(cont);
357 let (val_str, _) = val.to_simple_r(cont);
358 let (typ, _, _) = typing(&cont, exp).to_tuple();
359 let res = match typ {
360 Type::Vec(_, _, _, _) => format!("{}[[{}]]", exp_str, val_str),
361 _ => "".to_string(),
362 };
363 (res, cont.clone())
364 }
365 Lang::GenFunc(func, _, _) => (
366 format!("function(x, ...) UseMethod('{}')", func.to_string()),
367 cont.clone(),
368 ),
369 Lang::Let(expr, ttype, body, _) => {
370 let (body_str, new_cont) = body.to_r(cont);
371 let new_name = format_backtick(expr.clone().to_r(cont).0);
372
373 let (r_code, _new_name2) = Function::try_from((**body).clone())
374 .map(|_| {
375 let related_type = typing(cont, expr).value;
376 let method = match cont.get_environment() {
377 Environment::Project => format!(
378 "#' @method {}\n",
379 new_name.replace(".", " ").replace("`", "")
380 ),
381 _ => "".to_string(),
382 };
383 match related_type {
384 Type::Empty(_) => {
385 (format!("{} <- {}", new_name, body_str), new_name.clone())
386 }
387 Type::Any(_) | Type::Generic(_, _) => (
388 format!("{}.default <- {}", new_name, body_str),
389 new_name.clone(),
390 ),
391 _ => (
392 format!("{}{} <- {}", method, new_name, body_str),
393 new_name.clone(),
394 ),
395 }
396 })
397 .unwrap_or((format!("{} <- {}", new_name, body_str), new_name));
398 let code = if !ttype.is_empty() {
399 let _ = new_cont.get_type_anotation(ttype);
400 format!("{}\n", r_code)
401 } else {
402 r_code + "\n"
403 };
404 (code, new_cont)
405 }
406 Lang::Array(_v, _h) => {
407 let typ = self.typing(cont).value;
408
409 let _dimension = ArrayType::try_from(typ.clone())
410 .unwrap()
411 .get_shape()
412 .map(|sha| format!("c({})", sha))
413 .unwrap_or(format!("c(0)"));
414
415 let array = &self
416 .linearize_array()
417 .iter()
418 .map(|lang| lang.to_r(&cont).0)
419 .collect::<Vec<_>>()
420 .join(", ")
421 .and_if(|lin_array| lin_array != "")
422 .map(|lin_array| format!("typed_vec({})", lin_array))
424 .unwrap_or("logical(0)".to_string());
425
426 (
427 format!("{} |> {}", array, cont.get_type_anotation(&typ)),
428 cont.to_owned(),
429 )
430 }
431 Lang::Record(args, _) => {
432 let (body, current_cont) = Translatable::from(cont.clone())
433 .join_arg_val(args, ",\n ")
434 .into();
435 let (typ, _, _) = typing(cont, self).to_tuple();
436 let anotation = cont.get_type_anotation(&typ);
437 cont.get_classes(&typ)
438 .map(|_| format!("list({}) |> {}", body, anotation))
439 .unwrap_or(format!("list({}) |> {}", body, anotation))
440 .to_some()
441 .map(|s| (s, current_cont))
442 .unwrap()
443 }
444 Lang::If(cond, exp, els, _) if els == &Box::new(Lang::Empty(HelpData::default())) => {
445 Translatable::from(cont.clone())
446 .add("if(")
447 .to_r(cond)
448 .add(") {\n")
449 .to_r(exp)
450 .add(" \n}")
451 .into()
452 }
453 Lang::If(cond, exp, els, _) => Translatable::from(cont.clone())
454 .add("if(")
455 .to_r(cond)
456 .add(") {\n")
457 .to_r(exp)
458 .add(" \n} else ")
459 .to_r(els)
460 .into(),
461 Lang::Tuple(vals, _) => Translatable::from(cont.clone())
462 .add("struct(list(")
463 .join(vals, ", ")
464 .add("), 'Tuple')")
465 .into(),
466 Lang::Assign(var, exp, _) => Translatable::from(cont.clone())
467 .to_r(var)
468 .add(" <- ")
469 .to_r(exp)
470 .into(),
471 Lang::Comment(txt, _) => ("#".to_string() + txt, cont.clone()),
472 Lang::Tag(s, t, _) => {
473 let (t_str, new_cont) = t.to_r(cont);
474 let (typ, _, _) = typing(cont, self).to_tuple();
475 let class = cont.get_class(&typ);
476 cont.get_classes(&typ)
477 .map(|res| {
478 format!(
479 "struct(list('{}', {}), c('Tag', {}, {}))",
480 s, t_str, class, res
481 )
482 })
483 .unwrap_or(format!(
484 "struct(list('{}', {}), c('Tag', {}))",
485 s, t_str, class
486 ))
487 .to_some()
488 .map(|s| (s, new_cont))
489 .unwrap()
490 }
491 Lang::Empty(_) => ("NA".to_string(), cont.clone()),
492 Lang::ModuleDecl(name, _) => (format!("{} <- new.env()", name), cont.clone()),
493 Lang::Lines(exps, _) => Translatable::from(cont.clone()).join(exps, "\n").into(),
494 Lang::Return(exp, _) => Translatable::from(cont.clone())
495 .add("return ")
496 .to_r(exp)
497 .into(),
498 Lang::Lambda(bloc, _) => (
499 format!("function(x) {{ {} }}", bloc.to_r(cont).0),
500 cont.clone(),
501 ),
502 Lang::VecBlock(bloc, _) => (bloc.to_string(), cont.clone()),
503 Lang::Library(name, _) => (format!("library({})", name), cont.clone()),
504 Lang::Match(exp, var, branches, _) => (
505 to_if_statement(var.clone(), (**exp).clone(), branches, cont),
506 cont.clone(),
507 ),
508 Lang::Exp(exp, _) => (exp.clone(), cont.clone()),
509 Lang::ForLoop(var, iterator, body, _) => Translatable::from(cont.clone())
510 .add("for (")
511 .to_r_safe(var)
512 .add(" in ")
513 .to_r_safe(iterator)
514 .add(") {\n")
515 .to_r_safe(body)
516 .add("\n}")
517 .into(),
518 Lang::RFunction(vars, body, _) => Translatable::from(cont.clone())
519 .add("function (")
520 .join(vars, ", ")
521 .add(") \n")
522 .add(&body)
523 .add("\n")
524 .into(),
525 Lang::Signature(_, _, _) => ("".to_string(), cont.clone()),
526 Lang::Alias(_, _, _, _) => ("".to_string(), cont.clone()),
527 Lang::KeyValue(k, v, _) => (format!("{} = {}", k, v.to_r(cont).0), cont.clone()),
528 Lang::Vector(vals, _) => {
529 let res = "c(".to_string()
530 + &vals
531 .iter()
532 .map(|x| x.to_r(cont).0)
533 .collect::<Vec<_>>()
534 .join(", ")
535 + ")";
536 (res, cont.to_owned())
537 }
538 Lang::Not(exp, _) => (format!("!{}", exp.to_r(cont).0), cont.clone()),
539 Lang::Sequence(vals, _) => {
540 let res = if vals.len() > 0 {
541 "c(".to_string()
542 + &vals
543 .iter()
544 .map(|x| "list(".to_string() + &x.to_r(cont).0 + ")")
545 .collect::<Vec<_>>()
546 .join(", ")
547 + ")"
548 } else {
549 "c(list())".to_string()
550 };
551 (res, cont.to_owned())
552 }
553 Lang::TestBlock(body, h) => {
554 let file_name = h
555 .get_file_data()
556 .map(|(name, _)| format!("test-{}", name))
557 .unwrap_or_else(|| "test-unknown".to_string())
558 .replace("TypR/", "")
559 .replace(".ty", ".R");
560
561 let file_path = format!("tests/testthat/{}", file_name);
562 let content = body.to_r(cont).0;
563
564 let _ = write_output_file(&file_path, &content);
565 ("".to_string(), cont.clone())
566 }
567 Lang::JSBlock(exp, _id, _h) => {
568 let js_cont = Context::default(); let res = exp.to_js(&js_cont).0;
570 (format!("'{}{}'", JS_HEADER, res), cont.clone())
571 }
572 Lang::WhileLoop(condition, body, _) => (
573 format!(
574 "while ({}) {{\n{}\n}}",
575 condition.to_r(cont).0,
576 body.to_r(cont).0
577 ),
578 cont.clone(),
579 ),
580 Lang::Break(_) => ("break".to_string(), cont.clone()),
581 Lang::Module(name, body, position, config, _) => {
582 let name = if (name == "main") && (config.environment == Environment::Project) {
583 "a_main"
584 } else {
585 name
586 };
587 let content = body
588 .iter()
589 .map(|lang| lang.to_r(cont).0)
590 .collect::<Vec<_>>()
591 .join("\n");
592 match (position, config.environment) {
593 (ModulePosition::Internal, _) => (content, cont.clone()),
594 (ModulePosition::External, Environment::Wasm) => {
596 let file_path = format!("{}.R", name);
597 let _ = write_output_file(&file_path, &content);
598 (content, cont.clone())
600 }
601 (ModulePosition::External, Environment::StandAlone)
602 | (ModulePosition::External, Environment::Repl) => {
603 let file_path = format!("{}.R", name);
604 let _ = write_output_file(&file_path, &content);
605 (format!("source('{}')", file_path), cont.clone())
606 }
607 (ModulePosition::External, Environment::Project) => {
608 let file_path = format!("R/{}.R", name);
609 let _ = write_output_file(&file_path, &content);
610 ("".to_string(), cont.clone())
611 }
612 }
613 }
614 _ => {
615 println!("This language structure won't transpile: {:?}", self);
616 ("".to_string(), cont.clone())
617 }
618 };
619
620 result
621 }
622}