1use crate::writer::{Writer, comment_lines, escape_char, escape_string, verbatim_lines};
10use alloc::format;
11use alloc::string::{String, ToString};
12use alloc::vec::Vec;
13use zerodds_lang_cst::c::{
14 Block, Decl, Define, Enum, Enumerator, Expr, File, Function, Include, Member, Param, Statement,
15 Struct, Type, Typedef, Union, Variable,
16};
17use zerodds_lang_cst::common::{Doc, Ident, Literal};
18
19#[must_use]
21pub fn file(file: &File) -> String {
22 let mut w = Writer::spaces4();
23 doc(&mut w, &file.doc);
24 if let Some(guard) = &file.guard {
25 let mut line = String::from("#ifndef ");
26 line.push_str(guard.as_str());
27 w.line(&line);
28 let mut define = String::from("#define ");
29 define.push_str(guard.as_str());
30 w.line(&define);
31 w.blank_line();
32 }
33 for include in &file.includes {
34 w.line(&include_text(include));
35 }
36 if !file.includes.is_empty() {
37 w.blank_line();
38 }
39 for (index, decl) in file.decls.iter().enumerate() {
40 if index > 0 {
41 w.blank_line();
42 }
43 render_decl(&mut w, decl);
44 }
45 if let Some(guard) = &file.guard {
46 w.blank_line();
47 let mut line = String::from("#endif /* ");
48 line.push_str(guard.as_str());
49 line.push_str(" */");
50 w.line(&line);
51 }
52 w.finish()
53}
54
55#[must_use]
57pub fn decl(node: &Decl) -> String {
58 let mut w = Writer::spaces4();
59 render_decl(&mut w, node);
60 w.finish()
61}
62
63#[must_use]
65pub fn type_(node: &Type) -> String {
66 type_text(node)
67}
68
69#[must_use]
74pub fn expr(node: &Expr) -> String {
75 expr_text(node)
76}
77
78pub(crate) fn include_text(include: &Include) -> String {
80 let mut out = String::from("#include ");
81 if include.system {
82 out.push('<');
83 out.push_str(&include.path);
84 out.push('>');
85 } else {
86 out.push('"');
87 out.push_str(&include.path);
88 out.push('"');
89 }
90 out
91}
92
93fn render_decl(w: &mut Writer, node: &Decl) {
94 match node {
95 Decl::Struct(s) => render_struct(w, s),
96 Decl::Union(u) => render_union(w, u),
97 Decl::Enum(e) => render_enum(w, e),
98 Decl::Typedef(t) => render_typedef(w, t),
99 Decl::Function(f) => render_function(w, f),
100 Decl::Variable(v) => render_variable(w, v),
101 Decl::Define(d) => render_define(w, d),
102 Decl::Comment(c) => doc(w, c),
103 Decl::Verbatim(text) => verbatim_lines(w, text),
104 }
105}
106
107fn render_struct(w: &mut Writer, node: &Struct) {
108 doc(w, &node.doc);
109 let mut head = String::from("struct");
110 if let Some(name) = &node.name {
111 head.push(' ');
112 head.push_str(name.as_str());
113 }
114 head.push_str(" {");
115 w.line(&head);
116 w.indent();
117 for text in &node.body_text.begin {
118 verbatim_lines(w, text);
119 }
120 for member in &node.members {
121 render_member(w, member);
122 }
123 for text in &node.body_text.end {
124 verbatim_lines(w, text);
125 }
126 w.dedent();
127 w.line("};");
128}
129
130fn render_union(w: &mut Writer, node: &Union) {
131 doc(w, &node.doc);
132 let mut head = String::from("union");
133 if let Some(name) = &node.name {
134 head.push(' ');
135 head.push_str(name.as_str());
136 }
137 head.push_str(" {");
138 w.line(&head);
139 w.indent();
140 for member in &node.members {
141 render_member(w, member);
142 }
143 w.dedent();
144 w.line("};");
145}
146
147fn render_member(w: &mut Writer, node: &Member) {
148 doc(w, &node.doc);
149 let mut line = declarator_text(&node.type_, node.name.as_str(), &node.dims);
150 if let Some(bits) = node.bits {
151 line.push_str(" : ");
152 line.push_str(&alloc::format!("{bits}"));
153 }
154 line.push(';');
155 w.line(&line);
156}
157
158fn declarator_text(type_: &Type, name: &str, dims: &[Expr]) -> String {
161 let mut out = type_text(type_);
162 if !out.ends_with('*') {
163 out.push(' ');
164 }
165 out.push_str(name);
166 for dim in dims {
167 out.push('[');
168 out.push_str(&expr_text(dim));
169 out.push(']');
170 }
171 out
172}
173
174fn render_enum(w: &mut Writer, node: &Enum) {
175 doc(w, &node.doc);
176 let mut head = String::from("enum");
177 if let Some(name) = &node.name {
178 head.push(' ');
179 head.push_str(name.as_str());
180 }
181 head.push_str(" {");
182 w.line(&head);
183 w.indent();
184 for (index, enumerator) in node.enumerators.iter().enumerate() {
185 render_enumerator(w, enumerator, index + 1 == node.enumerators.len());
186 }
187 w.dedent();
188 w.line("};");
189}
190
191fn render_enumerator(w: &mut Writer, node: &Enumerator, last: bool) {
192 doc(w, &node.doc);
193 let mut line = String::from(node.name.as_str());
194 if let Some(value) = &node.value {
195 line.push_str(" = ");
196 line.push_str(&expr_text(value));
197 }
198 if !last {
199 line.push(',');
200 }
201 w.line(&line);
202}
203
204fn render_typedef(w: &mut Writer, node: &Typedef) {
205 doc(w, &node.doc);
206 let mut line = String::from("typedef ");
207 line.push_str(&declarator_text(
208 &node.type_,
209 node.name.as_str(),
210 &node.dims,
211 ));
212 line.push(';');
213 w.line(&line);
214}
215
216fn render_function(w: &mut Writer, node: &Function) {
217 doc(w, &node.doc);
218 let mut head = String::new();
219 if node.is_static {
220 head.push_str("static ");
221 }
222 if node.is_inline {
223 head.push_str("inline ");
224 }
225 if node.is_maybe_unused {
229 head.push_str("__attribute__((unused)) ");
230 }
231 head.push_str(&type_text(&node.returns));
232 if !head.ends_with('*') {
233 head.push(' ');
234 }
235 head.push_str(node.name.as_str());
236 head.push('(');
237 head.push_str(¶ms_text(&node.params));
238 head.push(')');
239 match &node.body {
240 None => {
241 head.push(';');
242 w.line(&head);
243 }
244 Some(block) => {
245 head.push_str(" {");
246 w.line(&head);
247 w.indent();
248 render_block(w, block);
249 w.dedent();
250 w.line("}");
251 }
252 }
253}
254
255fn params_text(params: &[Param]) -> String {
258 if params.is_empty() {
259 return "void".to_string();
260 }
261 let parts: Vec<String> = params
262 .iter()
263 .map(|param| match ¶m.name {
264 Some(name) => declarator_text(¶m.type_, name.as_str(), &[]),
265 None => type_text(¶m.type_),
266 })
267 .collect();
268 parts.join(", ")
269}
270
271fn render_variable(w: &mut Writer, node: &Variable) {
272 doc(w, &node.doc);
273 let mut line = String::new();
274 if node.is_static {
275 line.push_str("static ");
276 }
277 if node.is_const {
278 line.push_str("const ");
279 }
280 line.push_str(&declarator_text(&node.type_, node.name.as_str(), &[]));
281 if let Some(value) = &node.value {
282 line.push_str(" = ");
283 line.push_str(&expr_text(value));
284 }
285 line.push(';');
286 w.line(&line);
287}
288
289fn render_define(w: &mut Writer, node: &Define) {
290 doc(w, &node.doc);
291 let mut line = String::from("#define ");
292 line.push_str(node.name.as_str());
293 if let Some(value) = &node.value {
294 line.push(' ');
295 line.push_str(&expr_text(value));
296 }
297 w.line(&line);
298}
299
300fn render_block(w: &mut Writer, block: &Block) {
307 for statement in &block.statements {
308 render_statement(w, statement);
309 }
310}
311
312fn render_if_chain(w: &mut Writer, node: &Statement, opener: &str) {
338 let mut node = node;
339 let mut opener = opener;
340 loop {
341 let Statement::If {
342 condition,
343 then,
344 otherwise,
345 } = node
346 else {
347 render_statement(w, node);
348 return;
349 };
350 let mut head = String::from(opener);
351 head.push_str(&expr_text(condition));
352 head.push_str(") {");
353 w.line(&head);
354 w.indent();
355 render_block(w, then);
356 w.dedent();
357 match otherwise {
358 Some(block) => match block.statements.as_slice() {
359 [nested @ Statement::If { .. }] => {
360 node = nested;
361 opener = "} else if (";
362 }
363 _ => {
364 w.line("} else {");
365 w.indent();
366 render_block(w, block);
367 w.dedent();
368 w.line("}");
369 return;
370 }
371 },
372 None => {
373 w.line("}");
374 return;
375 }
376 }
377 }
378}
379
380fn render_statement(w: &mut Writer, node: &Statement) {
387 match node {
388 Statement::Declare { type_, name, value } => {
389 let mut line = declarator_text(type_, name.as_str(), &[]);
390 if let Some(value) = value {
391 line.push_str(" = ");
392 line.push_str(&expr_text(value));
393 }
394 line.push(';');
395 w.line(&line);
396 }
397 Statement::Expr(value) => {
398 let mut line = expr_text(value);
399 line.push(';');
400 w.line(&line);
401 }
402 Statement::Return(value) => {
403 let mut line = String::from("return");
404 if let Some(value) = value {
405 line.push(' ');
406 line.push_str(&expr_text(value));
407 }
408 line.push(';');
409 w.line(&line);
410 }
411 Statement::If { .. } => render_if_chain(w, node, "if ("),
412 Statement::For {
413 init,
414 condition,
415 step,
416 body,
417 } => {
418 let mut head = String::from("for (");
419 head.push_str(statement_inline(init).trim_end_matches(';'));
420 head.push_str("; ");
421 head.push_str(&expr_text(condition));
422 head.push_str("; ");
423 head.push_str(&expr_text(step));
424 head.push_str(") {");
425 w.line(&head);
426 w.indent();
427 render_block(w, body);
428 w.dedent();
429 w.line("}");
430 }
431 Statement::While { condition, body } => {
432 let mut head = String::from("while (");
433 head.push_str(&expr_text(condition));
434 head.push_str(") {");
435 w.line(&head);
436 w.indent();
437 render_block(w, body);
438 w.dedent();
439 w.line("}");
440 }
441 Statement::Break => w.line("break;"),
442 Statement::Comment(c) => doc(w, c),
443 }
444}
445
446fn statement_inline(node: &Statement) -> String {
448 match node {
449 Statement::Declare { type_, name, value } => {
450 let mut out = declarator_text(type_, name.as_str(), &[]);
451 if let Some(value) = value {
452 out.push_str(" = ");
453 out.push_str(&expr_text(value));
454 }
455 out
456 }
457 Statement::Expr(value) => expr_text(value),
458 _ => String::new(),
459 }
460}
461
462fn type_text(node: &Type) -> String {
468 match node {
469 Type::Primitive(primitive) => primitive.as_str().to_string(),
470 Type::Named(name) => name.as_str().to_string(),
471 Type::StructTag(name) => {
472 let mut out = String::from("struct ");
473 out.push_str(name.as_str());
474 out
475 }
476 Type::UnionTag(name) => {
477 let mut out = String::from("union ");
478 out.push_str(name.as_str());
479 out
480 }
481 Type::EnumTag(name) => {
482 let mut out = String::from("enum ");
483 out.push_str(name.as_str());
484 out
485 }
486 Type::Pointer(target) => {
487 let mut out = type_text(target);
488 if !out.ends_with('*') {
489 out.push(' ');
490 }
491 out.push('*');
492 out
493 }
494 Type::Const(target) => {
495 let mut out = String::from("const ");
496 out.push_str(&type_text(target));
497 out
498 }
499 Type::Void => "void".to_string(),
500 }
501}
502
503fn expr_text(node: &Expr) -> String {
510 match node {
511 Expr::Literal(literal) => literal_text(literal),
512 Expr::Name(name) => name.as_str().to_string(),
513 Expr::Call { callee, args } => {
514 let mut out = expr_text(callee);
515 out.push('(');
516 let parts: Vec<String> = args.iter().map(expr_text).collect();
517 out.push_str(&parts.join(", "));
518 out.push(')');
519 out
520 }
521 Expr::Member { value, name } => {
522 let mut out = expr_text(value);
523 out.push('.');
524 out.push_str(name.as_str());
525 out
526 }
527 Expr::Arrow { value, name } => {
528 let mut out = expr_text(value);
529 out.push_str("->");
530 out.push_str(name.as_str());
531 out
532 }
533 Expr::Index { value, index } => {
534 let mut out = expr_text(value);
535 out.push('[');
536 out.push_str(&expr_text(index));
537 out.push(']');
538 out
539 }
540 Expr::Unary { op, value } => {
541 let mut out = String::from(*op);
542 out.push_str(&expr_text(value));
543 out
544 }
545 Expr::AddressOf(value) => {
546 let mut out = String::from("&");
547 out.push_str(&expr_text(value));
548 out
549 }
550 Expr::Deref(value) => {
551 let mut out = String::from("*");
552 out.push_str(&expr_text(value));
553 out
554 }
555 Expr::Binary { op, lhs, rhs } => {
556 let mut out = expr_text(lhs);
557 out.push(' ');
558 out.push_str(op);
559 out.push(' ');
560 out.push_str(&expr_text(rhs));
561 out
562 }
563 Expr::Cast { type_, value } => {
564 let mut out = String::from("(");
565 out.push_str(&type_text(type_));
566 out.push(')');
567 out.push_str(&expr_text(value));
568 out
569 }
570 Expr::Paren(inner) => {
571 let mut out = String::from("(");
572 out.push_str(&expr_text(inner));
573 out.push(')');
574 out
575 }
576 Expr::SizeOf(type_) => {
577 let mut out = String::from("sizeof(");
578 out.push_str(&type_text(type_));
579 out.push(')');
580 out
581 }
582 Expr::Initializer(elements) => {
583 let mut out = String::from("{ ");
584 let parts: Vec<String> = elements.iter().map(expr_text).collect();
585 out.push_str(&parts.join(", "));
586 out.push_str(" }");
587 out
588 }
589 }
590}
591
592fn literal_text(literal: &Literal) -> String {
593 match literal {
594 Literal::Int(text) | Literal::Float(text) => text.clone(),
595 Literal::Bool(value) => {
596 if *value {
597 "true".to_string()
598 } else {
599 "false".to_string()
600 }
601 }
602 Literal::Char(ch) => {
603 let mut out = String::from("'");
604 out.push_str(&escape_char(*ch));
605 out.push('\'');
606 out
607 }
608 Literal::Str(text) => {
609 let mut out = String::from("\"");
610 out.push_str(&escape_string(text));
611 out.push('"');
612 out
613 }
614 Literal::Bytes(bytes) => {
615 let mut out = String::from("{ ");
616 let parts: Vec<String> = bytes.iter().map(|b| alloc::format!("0x{b:02X}")).collect();
617 out.push_str(&parts.join(", "));
618 out.push_str(" }");
619 out
620 }
621 Literal::Null => "NULL".to_string(),
622 }
623}
624
625fn doc(w: &mut Writer, doc: &Doc) {
626 if doc.is_empty() {
627 return;
628 }
629 comment_lines(w, "// ", doc.as_lines());
633}
634
635#[must_use]
637pub fn ident(name: &Ident) -> String {
638 name.as_str().to_string()
639}
640
641#[must_use]
660pub fn integer_text(value: i128) -> String {
661 if value == i128::from(i64::MIN) {
662 return "(-9223372036854775807LL - 1)".to_string();
663 }
664 format!("{value}")
665}