orbitc/
lib.rs

1use std::{
2    ffi::OsStr,
3    fs::write,
4    io::{self, Write},
5    path::Path,
6    process::Command,
7};
8
9use c_emit::{CArg, Code as CCode, VarInit};
10use tempfile::NamedTempFile;
11
12#[derive(Debug, Clone)]
13pub struct Code<S: AsRef<str>> {
14    pub lines: Vec<CodeLine<S>>,
15}
16
17#[derive(Debug, Clone)]
18pub enum CodeLine<S: AsRef<str>> {
19    Printf(Vec<Arg<S>>),
20    Scanf { name: S, prompt: S },
21    Let { name: S, value: Arg<S> },
22}
23
24#[derive(Debug, Clone, Copy)]
25pub enum Arg<S: AsRef<str>> {
26    String(S),
27    Ident(S),
28    Int32(i32),
29    Int64(i64),
30    Float(f32),
31    Double(f64),
32    Bool(bool),
33    Char(char),
34}
35
36impl<S: AsRef<str>> Code<S> {
37    pub fn transpile(&self) -> String {
38        let mut c = CCode::new();
39
40        for line in &self.lines {
41            match line {
42                CodeLine::Printf(v) => {
43                    c.include("stdio.h");
44
45                    let mut root = match v.first().expect("Couldn't find root string!") {
46                        Arg::String(s) => s.as_ref(),
47                        _ => panic!("Couldn't find root string!"),
48                    }
49                    .to_string();
50
51                    if v.len() > 1 {
52                        let mut subs = vec![];
53
54                        for sub in &v[1..] {
55                            subs.push(match sub {
56                                Arg::String(s) => {
57                                    root.push_str("%s");
58                                    CArg::String(s.as_ref())
59                                }
60                                Arg::Ident(ident) => {
61                                    root.push_str("%s");
62                                    CArg::Ident(ident.as_ref())
63                                }
64                                Arg::Int32(n) => {
65                                    root.push_str("%d");
66                                    CArg::Int32(*n)
67                                }
68                                Arg::Int64(n) => {
69                                    root.push_str("%d");
70                                    CArg::Int64(*n)
71                                }
72                                Arg::Float(f) => {
73                                    root.push_str("%f");
74                                    CArg::Float(*f)
75                                }
76                                Arg::Double(f) => {
77                                    root.push_str("%lf");
78                                    CArg::Double(*f)
79                                }
80                                Arg::Bool(b) => {
81                                    root.push_str("%d");
82                                    CArg::Bool(*b)
83                                }
84                                Arg::Char(c) => {
85                                    root.push_str("%c");
86                                    CArg::Char(*c)
87                                }
88                            });
89                        }
90
91                        let mut args = vec![CArg::String(&root)];
92
93                        args.extend(subs);
94
95                        c.call_func_with_args("printf", args);
96                    } else {
97                        c.call_func_with_args("printf", vec![CArg::String(&root)]);
98                    }
99                }
100                CodeLine::Scanf { name, prompt } => {
101                    c.include("stdio.h");
102
103                    c.new_var(name, VarInit::SizeString(1024));
104                    c.call_func_with_args("printf", vec![CArg::String(prompt.as_ref())]);
105
106                    c.call_func_with_args(
107                        "scanf",
108                        vec![CArg::String("%s"), CArg::Ident(name.as_ref())],
109                    );
110                }
111                CodeLine::Let { name, value } => {
112                    let name = name.as_ref();
113
114                    match value {
115                        Arg::String(s) => {
116                            let s = s.as_ref();
117                            c.new_var(name, VarInit::String(s));
118                        }
119                        Arg::Ident(_) => todo!(),
120                        Arg::Int32(i) => {
121                            c.new_var(name, VarInit::Int32(*i));
122                        }
123                        Arg::Int64(i) => {
124                            c.new_var(name, VarInit::Int64(*i));
125                        }
126                        Arg::Float(f) => {
127                            c.new_var(name, VarInit::Float(*f));
128                        }
129                        Arg::Double(f) => {
130                            c.new_var(name, VarInit::Double(*f));
131                        }
132                        Arg::Bool(b) => {
133                            c.new_var(name, VarInit::Bool(*b));
134                        }
135                        Arg::Char(c_) => {
136                            c.new_var(name, VarInit::Char(*c_));
137                        }
138                    };
139                }
140            }
141        }
142
143        c.to_string()
144    }
145
146    pub fn export_c<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
147        let path = path.as_ref();
148        let contents = self.transpile();
149
150        write(path, contents)
151    }
152
153    pub fn build<O: AsRef<OsStr>>(&self, path: O) -> io::Result<NamedTempFile> {
154        let path = path.as_ref();
155        let tmp = NamedTempFile::new()?;
156
157        let tmpath = tmp.path();
158
159        self.export_c(tmpath)?;
160
161        let output = Command::new("gcc")
162            .arg("-x")
163            .arg("c")
164            .arg(tmpath)
165            .arg("-o")
166            .arg(path)
167            .output()
168            .expect("Couldn't compile correctly!");
169
170        io::stdout().write_all(&output.stdout)?;
171        io::stderr().write_all(&output.stderr)?;
172
173        Ok(tmp)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use crate::Arg::String;
180
181    use super::{Arg, Code, CodeLine};
182
183    #[test]
184    fn test_printf() {
185        let c = Code {
186            lines: vec![CodeLine::Printf(vec![String("Hello, "), String("world")])],
187        };
188
189        assert_eq!(
190            c.transpile(),
191            r##"#include<stdio.h>
192int main() {
193printf("Hello, %s","world");
194return 0;
195}
196"##
197        )
198    }
199
200    #[test]
201    fn test_scanf() {
202        let c = Code {
203            lines: vec![CodeLine::Scanf {
204                name: "a",
205                prompt: "Enter name: ",
206            }],
207        };
208
209        assert_eq!(
210            c.transpile(),
211            r##"#include<stdio.h>
212int main() {
213char a[1024];
214printf("Enter name: ");
215scanf("%s",a);
216return 0;
217}
218"##
219        )
220    }
221
222    #[test]
223    fn test_let_string() {
224        let c = Code {
225            lines: vec![CodeLine::Let {
226                name: "s",
227                value: Arg::String("Hello, world!"),
228            }],
229        };
230
231        assert_eq!(
232            c.transpile(),
233            r#"int main() {
234char s[]="Hello, world!";
235return 0;
236}
237"#
238        )
239    }
240
241    #[test]
242    fn test_let_i32() {
243        let c = Code {
244            lines: vec![CodeLine::Let {
245                name: "i",
246                value: Arg::Int32(i32::MAX),
247            }],
248        };
249
250        assert_eq!(
251            c.transpile(),
252            format!(
253                r#"int main() {{
254int i={};
255return 0;
256}}
257"#,
258                i32::MAX
259            )
260        )
261    }
262
263    #[test]
264    fn test_let_i64() {
265        let c = Code {
266            lines: vec![CodeLine::Let {
267                name: "i",
268                value: Arg::Int64(i64::MAX),
269            }],
270        };
271
272        assert_eq!(
273            c.transpile(),
274            format!(
275                r#"int main() {{
276int i={};
277return 0;
278}}
279"#,
280                i64::MAX
281            )
282        )
283    }
284
285    #[test]
286    fn test_let_float() {
287        let c = Code {
288            lines: vec![CodeLine::Let {
289                name: "f",
290                value: Arg::Float(f32::MAX),
291            }],
292        };
293
294        assert_eq!(
295            c.transpile(),
296            format!(
297                r#"int main() {{
298float f={};
299return 0;
300}}
301"#,
302                f32::MAX
303            )
304        )
305    }
306
307    #[test]
308    fn test_let_double() {
309        let c = Code {
310            lines: vec![CodeLine::Let {
311                name: "f",
312                value: Arg::Double(f64::MAX),
313            }],
314        };
315
316        assert_eq!(
317            c.transpile(),
318            format!(
319                r#"int main() {{
320double f={};
321return 0;
322}}
323"#,
324                f64::MAX
325            )
326        )
327    }
328
329    #[test]
330    fn test_let_bool() {
331        let c = Code {
332            lines: vec![CodeLine::Let {
333                name: "b",
334                value: Arg::Bool(true),
335            }],
336        };
337
338        assert_eq!(
339            c.transpile(),
340            r#"#include<stdbool.h>
341int main() {
342bool b=true;
343return 0;
344}
345"#
346        )
347    }
348
349    #[test]
350    fn test_let_char() {
351        let c = Code {
352            lines: vec![CodeLine::Let {
353                name: "c",
354                value: Arg::Char('c'),
355            }],
356        };
357
358        assert_eq!(
359            c.transpile(),
360            r#"int main() {
361char c='c';
362return 0;
363}
364"#
365        )
366    }
367}