1pub mod cg;
19
20use std::{
21 fs::OpenOptions,
22 io::{BufWriter, Read, Write},
23 path::{Path, PathBuf},
24};
25
26use crate::mel::{
27 analysis::Analyzed,
28 ast::{AstVisitorDriver, Expr},
29 c::cg::{
30 LocatableString, MelCodegen, MelCodegenContext, MelCodegenError, MelCodegenLocatableError,
31 mel_type_to_c_type,
32 },
33};
34
35#[cfg(test)]
36mod tests;
37
38#[allow(clippy::result_large_err)]
39pub fn codegen<A: Write>(
40 expr: &Expr<Analyzed>,
41 context: MelCodegenContext,
42 mut output: A,
43) -> Result<A, MelCodegenLocatableError> {
44 let driver = AstVisitorDriver {};
45 let visitor = MelCodegen {};
46
47 let result = driver.visit(expr, &visitor, context)?;
48
49 for c in &result.code {
50 if let Err(e) = write!(output, "// {}\n{}\n", c.l, c.s) {
51 return Err(MelCodegenLocatableError {
52 location: expr.location().clone(),
53 error: MelCodegenError::WriteFailed(Box::new(e)),
54 context: result,
55 });
56 }
57 }
58
59 Ok(output)
60}
61
62#[allow(clippy::result_large_err)]
63pub fn codegen_function<A: Write>(
64 expr: &Expr<Analyzed>,
65 context: MelCodegenContext,
66 function_name: &str,
67 params: Vec<(String, String)>,
68 mut output: A,
69) -> Result<A, MelCodegenLocatableError> {
70 let driver = AstVisitorDriver {};
71 let visitor = MelCodegen {};
72
73 if let Err(e) = writeln!(
74 output,
75 "{} {}({}) {{",
76 mel_type_to_c_type(&expr.tipe()),
77 function_name,
78 params
79 .iter()
80 .map(|(tpe, name)| { format!("{} {}", tpe, name) })
81 .collect::<Vec<_>>()
82 .join(",")
83 ) {
84 return Err(MelCodegenLocatableError {
85 location: expr.location().clone(),
86 error: MelCodegenError::WriteFailed(Box::new(e)),
87 context,
88 });
89 };
90
91 let mut context = driver.visit(expr, &visitor, context)?;
92
93 let ret = format!("return {};", context.ssa);
95
96 context = context.append_code(LocatableString {
97 s: ret,
98 l: expr.location().clone(),
99 });
100
101 for c in &context.code {
102 if let Err(e) = write!(output, "// {}\n{}\n", c.l, c.s) {
103 return Err(MelCodegenLocatableError {
104 location: expr.location().clone(),
105 error: MelCodegenError::WriteFailed(Box::new(e)),
106 context,
107 });
108 }
109 }
110
111 writeln!(output, "}}").map_err(|e| MelCodegenLocatableError {
112 location: expr.location().clone(),
113 error: MelCodegenError::WriteFailed(Box::new(e)),
114 context,
115 })?;
116 Ok(output)
117}
118
119#[allow(clippy::result_large_err)]
120pub fn codegen_project(
121 expr: &Expr<Analyzed>,
122 context: MelCodegenContext,
123 function_name: &str,
124 params: Vec<(String, String)>,
125 output_path: &Path,
126 materials_path: &Path,
127) -> Result<(), MelCodegenLocatableError> {
128 let driver = AstVisitorDriver {};
129 let visitor = MelCodegen {};
130
131 let mut skeleton_contents_path = materials_path.to_path_buf();
132 skeleton_contents_path.push("mel.cpp");
133
134 let mut skeleton_contents: Vec<u8> = vec![];
135 OpenOptions::new()
136 .read(true)
137 .open(skeleton_contents_path)
138 .expect("Could not open skeleton file.")
139 .read_to_end(&mut skeleton_contents)
140 .expect("Could not read string from skeleton file");
141
142 let skeleton_contents =
143 String::from_utf8(skeleton_contents).expect("Could not convert source to string");
144
145 let function_data: Vec<u8> = vec![];
146 let mut function_buffer = BufWriter::new(function_data);
147
148 if let Err(e) = writeln!(
149 function_buffer,
150 "{} {}({}) {{",
151 mel_type_to_c_type(&expr.tipe()),
152 function_name,
153 params
154 .iter()
155 .map(|(tpe, name)| { format!("{} {}", tpe, name) })
156 .collect::<Vec<_>>()
157 .join(",")
158 ) {
159 return Err(MelCodegenLocatableError {
160 location: expr.location().clone(),
161 error: MelCodegenError::WriteFailed(Box::new(e)),
162 context,
163 });
164 };
165
166 let mut context = driver.visit(expr, &visitor, context)?;
167
168 let ret = format!("return {};", context.ssa);
170
171 context = context.append_code(LocatableString {
172 s: ret,
173 l: expr.location().clone(),
174 });
175
176 for c in &context.code {
177 if let Err(e) = write!(function_buffer, "\t// {}\n\t{}\n", c.l, c.s) {
178 return Err(MelCodegenLocatableError {
179 location: expr.location().clone(),
180 error: MelCodegenError::WriteFailed(Box::new(e)),
181 context,
182 });
183 }
184 }
185
186 writeln!(function_buffer, "}}").map_err(|e| MelCodegenLocatableError {
187 location: expr.location().clone(),
188 error: MelCodegenError::WriteFailed(Box::new(e)),
189 context,
190 })?;
191
192 let function_code = String::from_utf8_lossy(function_buffer.buffer());
193
194 let skeleton_contents = skeleton_contents.replace("INTERPRET_FUNCTION", &function_code);
195
196 let mut skeleton_output_path = output_path.to_path_buf();
197 skeleton_output_path.push(PathBuf::from("mel.cpp"));
198
199 let mut output = OpenOptions::new()
200 .read(true)
201 .write(true)
202 .create(true)
203 .truncate(true)
204 .open(skeleton_output_path)
205 .expect("Could not open output skeleton file");
206
207 output
208 .write_all(skeleton_contents.as_bytes())
209 .expect("Could not write to output file");
210
211 let mut cmake_path = materials_path.to_path_buf();
212 cmake_path.push("CMakeLists.txt");
213 let mut cmake_output_path = output_path.to_path_buf();
214 cmake_output_path.push("CMakeLists.txt");
215 std::fs::copy(cmake_path, cmake_output_path).expect("Could not copy CMakeLists.txt");
216
217 Ok(())
218}