1use std::fmt::Debug;
19
20use crate::common::TextLocation;
21use crate::environment::scope::Scopes;
22use crate::logging::{LogLevel, LogMsg, LogMsgs};
23
24use crate::mel::ast::{ComparisonOperator, LogicOperator, MathOperator, StringConcatOperator};
25use crate::mel::{
26 analysis::Analyzed,
27 ast::{
28 self, Argument, ArgumentList, AstVisitor, AstVisitorDriver, AstVisitorResult, BinaryExpr,
29 FunctionCall, IPAddressLiteral, Identifier, NumberLiteral, RegexLiteral, StringLiteral,
30 TernaryExpr,
31 },
32 types::Type,
33};
34
35use std::error::Error;
36
37#[derive(Debug)]
38pub enum MelCodegenError {
39 WriteFailed(Box<dyn Error>),
40 UnknownIdentifier(String),
41}
42
43#[derive(Debug)]
44pub struct MelCodegenLocatableError {
45 pub location: TextLocation,
46 pub error: MelCodegenError,
47 pub context: MelCodegenContext,
48}
49
50pub type MelCodegenResult = Result<MelCodegenContext, MelCodegenLocatableError>;
51
52#[derive(Clone, Debug)]
53pub struct LocatableString {
54 pub s: String,
55 pub l: TextLocation,
56}
57
58#[derive(Clone, Debug, Default)]
59pub struct SSA {
60 next: usize,
61 handle: String,
62}
63
64impl SSA {
65 pub fn new(handle: &str) -> Self {
66 SSA {
67 next: 0usize,
68 handle: handle.to_string(),
69 }
70 }
71
72 pub fn usse(&self) -> (String, Self) {
73 let mut next = self.clone();
74 let next_handle = format!("{}_{}", self.handle, self.next);
75 next.next += 1;
76
77 (next_handle, next)
78 }
79}
80
81#[derive(Debug, Default)]
82pub struct MelCodegenContext {
83 pub scopes: Scopes<Type>,
84 pub code: Vec<LocatableString>,
85 pub ssa_gen: SSA,
86 pub ssa: String,
87 pub log: LogMsgs,
88}
89
90impl MelCodegenContext {
91 pub fn update_log(&self, new: LogMsgs) -> Self {
92 MelCodegenContext {
93 code: self.code.clone(),
94 scopes: self.scopes.clone(),
95 ssa: self.ssa.clone(),
96 ssa_gen: self.ssa_gen.clone(),
97 log: new,
98 }
99 }
100
101 pub fn append_code(self, code: LocatableString) -> Self {
102 let mut nc = self.code.clone();
103 nc.push(code);
104 MelCodegenContext {
105 code: nc,
106 scopes: self.scopes,
107 log: self.log,
108 ssa: self.ssa,
109 ssa_gen: self.ssa_gen,
110 }
111 }
112
113 pub fn used_ssa(self, ssa: String) -> Self {
114 MelCodegenContext {
115 code: self.code,
116 scopes: self.scopes,
117 log: self.log,
118 ssa,
119 ssa_gen: self.ssa_gen,
120 }
121 }
122
123 pub fn next_ssa(self) -> (String, Self) {
124 let (next_handle, next_ssa) = self.ssa_gen.usse();
125 (
126 next_handle,
127 MelCodegenContext {
128 code: self.code,
129 scopes: self.scopes,
130 log: self.log,
131 ssa: self.ssa,
132 ssa_gen: next_ssa,
133 },
134 )
135 }
136}
137
138pub(crate) fn mel_type_to_c_type(tipe: &Type) -> String {
139 match tipe {
140 Type::Unsigned => "unsigned int".to_string(),
141 Type::Boolean => "bool".to_string(),
142 Type::Integer => "int".to_string(),
143 Type::Real => "double".to_string(),
144 Type::String => "std::string".to_string(),
145 Type::Regex => "std::regex".to_string(),
146 Type::Struct(s) => s.name.clone(),
147 Type::IPAddress => todo!(),
148 Type::Params(_) => todo!(),
149 Type::Function(_, _, _) => todo!(),
150 Type::Nil => todo!(),
151 }
152}
153
154fn mel_regex_to_c_regex(re: &str) -> String {
155 re.trim_start_matches("/").trim_end_matches("/").to_string()
156}
157
158fn mel_logic_operator_to_c_logic_operator(op: &LogicOperator, left: &str, right: &str) -> String {
159 let op = match op {
160 LogicOperator::And => "&&".to_string(),
161 LogicOperator::Or => "||".to_string(),
162 };
163
164 format!("{left} {op} {right}")
165}
166
167fn mel_comparison_operator_to_c_comparison_operator(
168 op: &ComparisonOperator,
169 left: &str,
170 right: &str,
171) -> String {
172 match op {
173 ComparisonOperator::Eq => format!("{left} == {right}"),
174 ComparisonOperator::Ne => format!("{left} != {right}"),
175 ComparisonOperator::Lt => format!("{left} < {right}"),
176 ComparisonOperator::Lte => format!("{left} <= {right}"),
177 ComparisonOperator::Gt => format!("{left} > {right}"),
178 ComparisonOperator::Gte => format!("{left} >= {right}"),
179 ComparisonOperator::Re => format!("std::regex_match({left}, {right})"),
180 ComparisonOperator::IP => todo!(),
181 }
182}
183
184fn mel_math_operator_to_c_math_operator(op: &MathOperator, left: &str, right: &str) -> String {
185 let op = match op {
186 MathOperator::Plus => "+".to_string(),
187 MathOperator::Minus => "-".to_string(),
188 MathOperator::Multiply => "*".to_string(),
189 MathOperator::Divide => "/".to_string(),
190 MathOperator::Modulo => "%".to_string(),
191 };
192 format!("{left} {op} {right}")
193}
194
195fn mel_concat_operator_to_c_concat_operator(
196 _: &StringConcatOperator,
197 left: &str,
198 right: &str,
199) -> String {
200 format!("{left} + {right}")
201}
202
203macro_rules! decl {
204 ($tpe:expr, $ssa:expr, $val:expr) => {
205 format!("{} {} = {};", mel_type_to_c_type(&$tpe), $ssa, $val)
206 };
207}
208
209macro_rules! use_log {
210 ($context:ident, $logx:expr) => {
211 match $context {
212 MelCodegenContext {
213 log,
214 scopes,
215 code,
216 ssa_gen,
217 ssa,
218 } => {
219 let logp = $logx(log);
220 MelCodegenContext {
221 log: logp,
222 scopes,
223 code,
224 ssa,
225 ssa_gen,
226 }
227 }
228 }
229 };
230}
231
232pub struct MelCodegen {}
233
234impl AstVisitor<MelCodegenContext, Analyzed, MelCodegenLocatableError> for MelCodegen {
235 fn visit_function_call(
236 &self,
237 ast: &FunctionCall<Analyzed>,
238 context: MelCodegenContext,
239 _driver: &AstVisitorDriver,
240 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
241 let _context = use_log!(context, |log: LogMsgs| {
242 trace_with_loc!(
243 log,
244 ast.location.clone(),
245 "Generating code for function call expression"
246 )
247 });
248
249 todo!()
250 }
251
252 fn visit_identifier(
253 &self,
254 ast: &Identifier<Analyzed>,
255 context: MelCodegenContext,
256 _driver: &AstVisitorDriver,
257 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
258 let context = use_log!(context, |log: LogMsgs| {
259 trace_with_loc!(
260 log,
261 ast.location.clone(),
262 "Generating code for identifier expression"
263 )
264 });
265
266 let (ssa, context) = context.next_ssa();
267
268 let found_id = match context.scopes.lookup(&ast.identifier) {
269 Some(v) => v,
270 None => {
271 return Err(MelCodegenLocatableError {
272 error: MelCodegenError::UnknownIdentifier(ast.identifier.clone()),
273 location: ast.location.clone(),
274 context,
275 });
276 }
277 };
278
279 let decl = decl!(found_id, ssa, ast.identifier);
280
281 let context = context.used_ssa(ssa);
282
283 let context = context.append_code(LocatableString {
284 s: decl,
285 l: ast.location.clone(),
286 });
287
288 Ok(context)
289 }
290
291 fn visit_argument_list(
292 &self,
293 ast: &ArgumentList<Analyzed>,
294 context: MelCodegenContext,
295 _driver: &AstVisitorDriver,
296 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
297 let _context = use_log!(context, |log: LogMsgs| {
298 trace_with_loc!(
299 log,
300 ast.location.clone(),
301 "Generating code for argument list expression"
302 )
303 });
304
305 todo!()
306 }
307
308 fn visit_argument(
309 &self,
310 ast: &Argument<Analyzed>,
311 context: MelCodegenContext,
312 _driver: &AstVisitorDriver,
313 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
314 let _context = use_log!(context, |log: LogMsgs| {
315 trace_with_loc!(
316 log,
317 ast.location.clone(),
318 "Generating code for argument list expression"
319 )
320 });
321
322 todo!()
323 }
324
325 fn visit_binary_expr(
326 &self,
327 ast: &BinaryExpr<Analyzed>,
328 context: MelCodegenContext,
329 driver: &AstVisitorDriver,
330 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
331 let context = use_log!(context, |log: LogMsgs| {
332 trace_with_loc!(
333 log,
334 ast.location.clone(),
335 "Generating code for binary expression"
336 )
337 });
338
339 let context = driver.visit(&ast.left, self, context)?;
340 let left_ssa = context.ssa.clone();
341
342 let context = driver.visit(&ast.right, self, context)?;
343 let right_ssa = context.ssa.clone();
344
345 let (ssa, mut context) = context.next_ssa();
346
347 let value = match &ast.op {
348 ast::BinaryInfixOperator::Logic(logic_operator) => {
349 mel_logic_operator_to_c_logic_operator(logic_operator, &left_ssa, &right_ssa)
350 }
351 ast::BinaryInfixOperator::Comparison(comparison_operator) => {
352 mel_comparison_operator_to_c_comparison_operator(
353 comparison_operator,
354 &left_ssa,
355 &right_ssa,
356 )
357 }
358 ast::BinaryInfixOperator::Math(math_operator) => {
359 mel_math_operator_to_c_math_operator(math_operator, &left_ssa, &right_ssa)
360 }
361 ast::BinaryInfixOperator::Concat(string_concat_operator) => {
362 mel_concat_operator_to_c_concat_operator(
363 string_concat_operator,
364 &left_ssa,
365 &right_ssa,
366 )
367 }
368 ast::BinaryInfixOperator::MemberAccess(_) => todo!(),
369 };
370
371 let decl = decl!(ast.aug.tipe, ssa, value);
372
373 context = context.used_ssa(ssa);
374
375 context = context.append_code(LocatableString {
376 s: decl,
377 l: ast.location.clone(),
378 });
379
380 Ok(context)
381 }
382
383 fn visit_literal(
384 &self,
385 ast: (&ast::Literal, &TextLocation, &Analyzed),
386 context: MelCodegenContext,
387 _driver: &AstVisitorDriver,
388 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
389 let context = use_log!(context, |log: LogMsgs| {
390 trace_with_loc!(log, ast.1.clone(), "Generating code for literal expression")
391 });
392
393 let (ssa, mut context) = context.next_ssa();
394
395 let lit = match ast {
396 (ast::Literal::Boolean(b), _, _) => LocatableString {
397 s: decl!(Type::Boolean, ssa, b.to_string()),
398 l: ast.1.clone(),
399 },
400 (ast::Literal::Number(NumberLiteral { literal: l }), _, _) => LocatableString {
401 s: decl!(Type::Integer, ssa, l.to_string()),
402 l: ast.1.clone(),
403 },
404 (ast::Literal::String(StringLiteral { literal: s }), _, _) => LocatableString {
405 s: decl!(Type::String, ssa, format!("\"{s}\"")),
406 l: ast.1.clone(),
407 },
408 (ast::Literal::Regex(RegexLiteral { literal: re }), _, _) => LocatableString {
409 s: decl!(
410 Type::Regex,
411 ssa,
412 format!("std::regex(\"{}\")", mel_regex_to_c_regex(&re.to_string()))
413 ),
414 l: ast.1.clone(),
415 },
416 (ast::Literal::IPAddress(IPAddressLiteral { literal: _ }), _, _) => todo!(),
417 };
418
419 context = context.used_ssa(ssa);
420
421 Ok(context.append_code(lit))
422 }
423
424 fn visit_ternary_expr(
425 &self,
426 ast: &TernaryExpr<Analyzed>,
427 context: MelCodegenContext,
428 driver: &AstVisitorDriver,
429 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
430 let context = use_log!(context, |log: LogMsgs| {
431 trace_with_loc!(
432 log,
433 ast.location.clone(),
434 "Generating code for literal expression"
435 )
436 });
437
438 let context = driver.visit(&ast.condition, self, context)?;
439 let condition_ssa = context.ssa.clone();
440
441 let context = driver.visit(&ast.yes, self, context)?;
442 let yes_ssa = context.ssa.clone();
443
444 let context = driver.visit(&ast.no, self, context)?;
445 let no_ssa = context.ssa.clone();
446
447 let (ssa, mut context) = context.next_ssa();
448
449 let value = format!("{} ? {} : {}", condition_ssa, yes_ssa, no_ssa);
450 let decl = decl!(ast.aug.tipe, ssa, value);
451
452 context = context.used_ssa(ssa);
453
454 context = context.append_code(LocatableString {
455 s: decl,
456 l: ast.location.clone(),
457 });
458
459 Ok(context)
460 }
461
462 fn visit_member_access_expr(
463 &self,
464 ast: &ast::MemberAccessExpression<Analyzed>,
465 context: MelCodegenContext,
466 _driver: &AstVisitorDriver,
467 ) -> AstVisitorResult<MelCodegenContext, MelCodegenLocatableError> {
468 let _context = use_log!(context, |log: LogMsgs| {
469 trace_with_loc!(
470 log,
471 ast.location.clone(),
472 "Generating code for member access expression"
473 )
474 });
475
476 todo!()
477 }
478}