sila_transpiler_infrastructure/
lib.rs

1use std::collections::HashMap;
2
3/// Supported platform to transpile
4#[derive(Debug, Clone)]
5enum Platform {
6    JavaScript,
7    Ruby,
8    Python,
9}
10
11/// Deta type
12#[derive(Debug, Clone)]
13pub enum Type {
14    /// 64 bit integer
15    Integer(i64),
16    /// 64 bit float number
17    Float(f64),
18    /// String of UTF-8
19    String(String),
20    /// Bool of true or false
21    Bool(bool),
22    /// Array doesn't mind about type
23    Array(Vec<Type>),
24    /// Dictionary or object
25    Dict(HashMap<Type, Type>),
26    /// Symbol such as key of the hashmap, object and dictionary, etc
27    Symbol(String),
28    /// Unique type that nothing value
29    None,
30}
31
32impl Type {
33    /// Generate the transpliled code
34    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
133/// Code block
134pub type Block = Vec<Instruction>;
135
136/// Program's normal instruction
137#[derive(Debug, Clone)]
138pub enum Instruction {
139    /// Standard output
140    Print(Expr),
141    /// Define variable
142    Let(String, Expr),
143    /// Define constance
144    Const(String, Expr),
145    /// Change variable's data
146    Variable(String, Expr),
147    /// If-else conditional branch
148    If(Expr, Block, Option<Block>),
149    /// While loop
150    While(Expr, Block),
151    /// Break the loop
152    Break,
153    /// Continue the loop
154    Continue,
155    /// Define function
156    Function(String, Vec<String>, Block),
157    /// Return value
158    Return(Option<Expr>),
159    /// Code comment
160    Comment(String),
161    /// Exception handling
162    TryError(Block, Block),
163    /// Import library
164    Import(String),
165}
166
167impl Instruction {
168    /// Generate the transpliled code
169    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/// Expression
357#[derive(Debug, Clone)]
358pub enum Expr {
359    /// Expression in the brackets
360    Expr(Vec<Expr>),
361    /// Operator of the expression
362    Operator(Operator),
363    /// Literal data
364    Literal(Type),
365    /// Variable reference
366    Variable(String),
367    /// Call user defined function
368    Call(String, Vec<Expr>),
369    /// Standrad library of the language
370    Library(Library, Vec<Expr>),
371}
372
373impl Expr {
374    /// Generate the transpliled code
375    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/// Operator of the expression
410#[derive(Debug, Clone)]
411pub enum Operator {
412    /// Addition
413    Add,
414    /// Subtraction
415    Sub,
416    /// Multiplication
417    Mul,
418    /// Division
419    Div,
420    /// Power
421    Pow,
422    /// Modulo
423    Mod,
424    /// Equal
425    Equal,
426    /// Not equal
427    NotEq,
428    /// Lessthan
429    Less,
430    /// Lessthan with equal
431    LessEq,
432    /// Greaterthan
433    Greater,
434    /// Greaterthan with equal
435    GreaterEq,
436    /// Logial and
437    And,
438    /// Logical or
439    Or,
440    /// Logial not
441    Not,
442}
443
444impl Operator {
445    /// Generate the transpliled code
446    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/// Standard library of the language
488#[derive(Debug, Clone)]
489pub enum Library {
490    /// Standard user input
491    Input,
492    /// Cast to string type
493    ToString,
494    /// Cast to string type
495    ToInterger,
496    /// Cast to float type
497    ToFloat,
498    /// Generate random float number between 0 and 1
499    Random,
500    /// Rounding off the float number to integer
501    Round,
502}
503
504impl Library {
505    /// Generate the transpliled code
506    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
537/// generate transpiled code of blocked
538fn 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
579/// Transpile to JavaScript
580pub fn transpile_javascript(program: Block) -> String {
581    format!(
582        "// Sila transpiled this code\n{}",
583        codegen_block(program, Platform::JavaScript, false)
584    )
585}
586
587/// Transpile to Ruby
588pub 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
595/// Transpile to python
596pub 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}