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}