1use std::collections::HashMap;
2
3#[derive(Debug, Clone)]
5enum Platform {
6 JavaScript,
7 Ruby,
8 Python,
9}
10
11#[derive(Debug, Clone)]
13pub enum Type {
14 Integer(i64),
16 Float(f64),
18 String(String),
20 Bool(bool),
22 Array(Vec<Type>),
24 Dict(HashMap<Type, Type>),
26 Symbol(String),
28 None,
30}
31
32impl Type {
33 fn codegen(&self, platform: Platform) -> String {
35 match platform.clone() {
36 Platform::JavaScript => format!(
37 "{}",
38 match self.clone() {
39 Type::Integer(i) => i.to_string(),
40 Type::Float(f) => f.to_string(),
41 Type::String(s) => format!("`{s}`"),
42 Type::Array(a) => format!(
43 "[{}]",
44 a.iter()
45 .map(|i| i.clone().codegen(platform.clone()))
46 .collect::<Vec<String>>()
47 .join(", ")
48 ),
49 Type::Bool(b) => b.to_string(),
50 Type::Dict(d) => format!(
51 "{{{}}}",
52 d.iter()
53 .map(|(k, v)| {
54 format!(
55 "{}:{}",
56 k.codegen(platform.clone()),
57 v.codegen(platform.clone())
58 )
59 })
60 .collect::<Vec<String>>()
61 .join(",\n")
62 ),
63 Type::Symbol(s) => s,
64 Type::None => "null".to_string(),
65 }
66 ),
67 Platform::Ruby => format!(
68 "{}",
69 match self.clone() {
70 Type::Integer(i) => i.to_string(),
71 Type::Float(f) => f.to_string(),
72 Type::String(s) => format!("\"{s}\""),
73 Type::Array(a) => format!(
74 "[{}]",
75 a.iter()
76 .map(|i| i.clone().codegen(platform.clone()))
77 .collect::<Vec<String>>()
78 .join(", ")
79 ),
80 Type::Bool(b) => b.to_string(),
81 Type::Dict(d) => format!(
82 "{{{}}}",
83 d.iter()
84 .map(|(k, v)| {
85 format!(
86 "{}:{}",
87 k.codegen(platform.clone()),
88 v.codegen(platform.clone())
89 )
90 })
91 .collect::<Vec<String>>()
92 .join(", ")
93 ),
94 Type::Symbol(s) => s,
95 Type::None => "nil".to_string(),
96 }
97 ),
98 Platform::Python => format!(
99 "{}",
100 match self.clone() {
101 Type::Integer(i) => i.to_string(),
102 Type::Float(f) => f.to_string(),
103 Type::String(s) => format!("'{s}'"),
104 Type::Array(a) => format!(
105 "[{}]",
106 a.iter()
107 .map(|i| i.clone().codegen(platform.clone()))
108 .collect::<Vec<String>>()
109 .join(", ")
110 ),
111 Type::Bool(b) => if b { "True" } else { "False" }.to_string(),
112 Type::Dict(d) => format!(
113 "{{{}}}",
114 d.iter()
115 .map(|(k, v)| {
116 format!(
117 "'{}': {}",
118 k.codegen(platform.clone()),
119 v.codegen(platform.clone())
120 )
121 })
122 .collect::<Vec<String>>()
123 .join(",")
124 ),
125 Type::Symbol(s) => s,
126 Type::None => "None".to_string(),
127 }
128 ),
129 }
130 }
131}
132
133pub type Block = Vec<Instruction>;
135
136#[derive(Debug, Clone)]
138pub enum Instruction {
139 Print(Expr),
141 Let(String, Expr),
143 Const(String, Expr),
145 Variable(String, Expr),
147 If(Expr, Block, Option<Block>),
149 While(Expr, Block),
151 Break,
153 Continue,
155 Function(String, Vec<String>, Block),
157 Return(Option<Expr>),
159 Comment(String),
161 TryError(Block, Block),
163 Import(String),
165}
166
167impl Instruction {
168 fn codegen(&self, platform: Platform) -> String {
170 match platform.clone() {
171 Platform::JavaScript => match self {
172 Instruction::Print(expr) => format!("console.log({})", expr.codegen(platform)),
173 Instruction::Let(name, value) => {
174 format!("let {name} = {}", value.codegen(platform))
175 }
176 Instruction::Const(name, value) => {
177 format!("const {name} = {}", value.codegen(platform))
178 }
179 Instruction::Variable(name, value) => {
180 format!("{name} = {}", value.codegen(platform))
181 }
182 Instruction::If(condition, true_block, false_block) => {
183 if let Some(false_block) = false_block {
184 format!(
185 "if {} {{\n{}\n}} else {{\n{}\n}}",
186 condition.codegen(platform.clone()),
187 codegen_block(true_block.clone(), platform.clone(), true),
188 codegen_block(false_block.clone(), platform, true)
189 )
190 } else {
191 format!(
192 "if {} {{\n{}\n}}",
193 condition.codegen(platform.clone()),
194 codegen_block(true_block.clone(), platform.clone(), true),
195 )
196 }
197 }
198 Instruction::While(condition, code_block) => format!(
199 "while {} {{\n{}\n}}",
200 condition.codegen(platform.clone()),
201 codegen_block(code_block.clone(), platform.clone(), true),
202 ),
203 Instruction::Break => "break".to_string(),
204 Instruction::Continue => "continue".to_string(),
205 Instruction::Function(name, args, code_block) => format!(
206 "function {name}({}) {{\n{}\n}}",
207 args.join(", "),
208 codegen_block(code_block.clone(), platform.clone(), true),
209 ),
210 Instruction::Return(v) => {
211 if v.clone().is_some() {
212 format!("return {}", v.clone().unwrap().codegen(platform.clone()))
213 } else {
214 "return".to_string()
215 }
216 }
217 Instruction::Comment(data) => {
218 if data.contains("\n") {
219 format!("/* {data} */")
220 } else {
221 format!("// {data}")
222 }
223 }
224 Instruction::TryError(try_code, handle_code) => format!(
225 "try {{\n{}\n}} catch {{\n{}\n}}",
226 codegen_block(try_code.clone(), platform.clone(), true),
227 codegen_block(handle_code.clone(), platform.clone(), true),
228 ),
229 Instruction::Import(name) => format!("import {name} from '{name}'"),
230 },
231 Platform::Ruby => match self {
232 Instruction::Print(expr) => format!("puts {}", expr.codegen(platform)),
233 Instruction::Let(name, value) => {
234 format!("{name} = {}", value.codegen(platform))
235 }
236 Instruction::Const(name, value) => {
237 format!("{name} = {}", value.codegen(platform))
238 }
239 Instruction::Variable(name, value) => {
240 format!("{name} = {}", value.codegen(platform))
241 }
242 Instruction::If(condition, true_block, false_block) => {
243 if let Some(false_block) = false_block {
244 format!(
245 "if {}\n{}\nelse\n{}\nend",
246 condition.codegen(platform.clone()),
247 codegen_block(true_block.clone(), platform.clone(), true),
248 codegen_block(false_block.clone(), platform, true)
249 )
250 } else {
251 format!(
252 "if {}\n{}\nend",
253 condition.codegen(platform.clone()),
254 codegen_block(true_block.clone(), platform.clone(), true),
255 )
256 }
257 }
258 Instruction::While(condition, code_block) => format!(
259 "while {} do\n{}\nend",
260 condition.codegen(platform.clone()),
261 codegen_block(code_block.clone(), platform.clone(), true),
262 ),
263 Instruction::Break => "break".to_string(),
264 Instruction::Continue => "next".to_string(),
265 Instruction::Function(name, args, code_block) => format!(
266 "def {name}({})\n{}\nend",
267 args.join(", "),
268 codegen_block(code_block.clone(), platform.clone(), true),
269 ),
270 Instruction::Return(v) => {
271 if v.clone().is_some() {
272 format!("return {}", v.clone().unwrap().codegen(platform.clone()))
273 } else {
274 "return".to_string()
275 }
276 }
277 Instruction::Comment(data) => {
278 if data.contains("\n") {
279 format!("=begin\n{data}\n=end")
280 } else {
281 format!("# {data}")
282 }
283 }
284 Instruction::TryError(try_code, handle_code) => format!(
285 "begin\n{}\nrescue\n{}\nend",
286 codegen_block(try_code.clone(), platform.clone(), true),
287 codegen_block(handle_code.clone(), platform.clone(), true),
288 ),
289 Instruction::Import(name) => format!("require {name}"),
290 },
291 Platform::Python => match self {
292 Instruction::Print(expr) => format!("print({})", expr.codegen(platform)),
293 Instruction::Let(name, value) => {
294 format!("{name} = {}", value.codegen(platform))
295 }
296 Instruction::Const(name, value) => {
297 format!("{name} = {}", value.codegen(platform))
298 }
299 Instruction::Variable(name, value) => {
300 format!("{name} = {}", value.codegen(platform))
301 }
302 Instruction::If(condition, true_block, false_block) => {
303 if let Some(false_block) = false_block {
304 format!(
305 "if {}:\n{}\nelse:\n{}",
306 condition.codegen(platform.clone()),
307 codegen_block(true_block.clone(), platform.clone(), true),
308 codegen_block(false_block.clone(), platform, true)
309 )
310 } else {
311 format!(
312 "if {}:\n{}",
313 condition.codegen(platform.clone()),
314 codegen_block(true_block.clone(), platform.clone(), true),
315 )
316 }
317 }
318
319 Instruction::While(condition, code_block) => format!(
320 "while {}:\n{}",
321 condition.codegen(platform.clone()),
322 codegen_block(code_block.clone(), platform.clone(), true),
323 ),
324 Instruction::Break => "break".to_string(),
325 Instruction::Continue => "continue".to_string(),
326 Instruction::Function(name, args, code_block) => format!(
327 "def {name}({}):\n{}",
328 args.join(", "),
329 codegen_block(code_block.clone(), platform.clone(), true),
330 ),
331 Instruction::Return(v) => {
332 if v.clone().is_some() {
333 format!("return {}", v.clone().unwrap().codegen(platform.clone()))
334 } else {
335 "return".to_string()
336 }
337 }
338 Instruction::Comment(data) => {
339 if data.contains("\n") {
340 format!("\"\"\"\n{data}\n\"\"\"")
341 } else {
342 format!("# {data}")
343 }
344 }
345 Instruction::TryError(try_code, handle_code) => format!(
346 "try:\n{}\nexcept:\n{}",
347 codegen_block(try_code.clone(), platform.clone(), true),
348 codegen_block(handle_code.clone(), platform.clone(), true),
349 ),
350 Instruction::Import(name) => format!("import {name}"),
351 },
352 }
353 }
354}
355
356#[derive(Debug, Clone)]
358pub enum Expr {
359 Expr(Vec<Expr>),
361 Operator(Operator),
363 Literal(Type),
365 Variable(String),
367 Call(String, Vec<Expr>),
369 Library(Library, Vec<Expr>),
371}
372
373impl Expr {
374 fn codegen(&self, platform: Platform) -> String {
376 match platform.clone() {
377 Platform::JavaScript | Platform::Ruby | Platform::Python => match self {
378 Expr::Expr(exprs) => format!(
379 "({})",
380 exprs
381 .iter()
382 .map(|i| i.codegen(platform.clone()))
383 .collect::<Vec<String>>()
384 .join(" ")
385 ),
386 Expr::Literal(value) => value.codegen(platform.clone()),
387 Expr::Operator(o) => o.codegen(platform.clone()),
388 Expr::Variable(v) => v.clone(),
389 Expr::Call(identify, args) => format!(
390 "{identify}({})",
391 args.iter()
392 .map(|i| i.codegen(platform.clone()))
393 .collect::<Vec<String>>()
394 .join(", ")
395 ),
396 Expr::Library(identify, args) => format!(
397 "{}({})",
398 identify.codegen(platform.clone()),
399 args.iter()
400 .map(|i| i.codegen(platform.clone()))
401 .collect::<Vec<String>>()
402 .join(", ")
403 ),
404 },
405 }
406 }
407}
408
409#[derive(Debug, Clone)]
411pub enum Operator {
412 Add,
414 Sub,
416 Mul,
418 Div,
420 Pow,
422 Mod,
424 Equal,
426 NotEq,
428 Less,
430 LessEq,
432 Greater,
434 GreaterEq,
436 And,
438 Or,
440 Not,
442}
443
444impl Operator {
445 fn codegen(&self, platform: Platform) -> String {
447 match platform {
448 Platform::JavaScript | Platform::Ruby => match self {
449 Operator::Add => "+",
450 Operator::Sub => "-",
451 Operator::Mul => "*",
452 Operator::Div => "/",
453 Operator::Pow => "**",
454 Operator::Mod => "%",
455 Operator::Equal => "==",
456 Operator::NotEq => "!=",
457 Operator::Less => "<",
458 Operator::LessEq => "<=",
459 Operator::Greater => ">",
460 Operator::GreaterEq => ">=",
461 Operator::And => "&&",
462 Operator::Or => "||",
463 Operator::Not => "!",
464 },
465 Platform::Python => match self {
466 Operator::Add => "+",
467 Operator::Sub => "-",
468 Operator::Mul => "*",
469 Operator::Div => "/",
470 Operator::Pow => "**",
471 Operator::Mod => "%",
472 Operator::Equal => "==",
473 Operator::NotEq => "!=",
474 Operator::Less => "<",
475 Operator::LessEq => "<=",
476 Operator::Greater => ">",
477 Operator::GreaterEq => ">=",
478 Operator::And => "and",
479 Operator::Or => "or",
480 Operator::Not => "not",
481 },
482 }
483 .to_string()
484 }
485}
486
487#[derive(Debug, Clone)]
489pub enum Library {
490 Input,
492 ToString,
494 ToInterger,
496 ToFloat,
498 Random,
500 Round,
502}
503
504impl Library {
505 fn codegen(&self, platform: Platform) -> String {
507 match platform.clone() {
508 Platform::JavaScript => match self {
509 Library::Input => "prompt",
510 Library::ToString => "String",
511 Library::ToInterger => "parseInt",
512 Library::ToFloat => "parseFloat",
513 Library::Random => "Math.random",
514 Library::Round => "Math.round",
515 },
516 Platform::Ruby => match self {
517 Library::Input => "input",
518 Library::ToString => "String",
519 Library::ToInterger => "Integer",
520 Library::ToFloat => "Float",
521 Library::Random => "rand",
522 Library::Round => "round",
523 },
524 Platform::Python => match self {
525 Library::Input => "input",
526 Library::ToString => "str",
527 Library::ToInterger => "int",
528 Library::ToFloat => "float",
529 Library::Random => "random.random",
530 Library::Round => "round",
531 },
532 }
533 .to_string()
534 }
535}
536
537fn codegen_block(program: Block, platform: Platform, indent: bool) -> String {
539 match platform {
540 Platform::JavaScript => format!(
541 "{}",
542 program
543 .iter()
544 .map(|x| x.codegen(platform.clone()))
545 .collect::<Vec<String>>()
546 .join(";\n")
547 .split("\n")
548 .collect::<Vec<&str>>()
549 .iter()
550 .map(|x| if indent {
551 " ".to_string() + &x
552 } else {
553 x.to_string()
554 })
555 .collect::<Vec<String>>()
556 .join("\n")
557 ),
558 Platform::Ruby | Platform::Python => format!(
559 "{}",
560 program
561 .iter()
562 .map(|x| x.codegen(platform.clone()))
563 .collect::<Vec<String>>()
564 .join("\n")
565 .split("\n")
566 .collect::<Vec<&str>>()
567 .iter()
568 .map(|x| if indent {
569 " ".to_string() + &x
570 } else {
571 x.to_string()
572 })
573 .collect::<Vec<String>>()
574 .join("\n")
575 ),
576 }
577}
578
579pub fn transpile_javascript(program: Block) -> String {
581 format!(
582 "// Sila transpiled this code\n{}",
583 codegen_block(program, Platform::JavaScript, false)
584 )
585}
586
587pub fn transpile_ruby(program: Block) -> String {
589 format!(
590 "# Sila transpiled this code\ndef input(prompt)\n print prompt\n return gets.chomp\nend\ndef round(n)\n n.round()\nend\n\n{}",
591 codegen_block(program, Platform::Ruby, false)
592 )
593}
594
595pub fn transpile_python(program: Block) -> String {
597 format!(
598 "# Sila transpiled this code\nimport random\n\n{}",
599 codegen_block(program, Platform::Python, false)
600 )
601}