1use crate::writer::{Writer, comment_lines, escape_string, verbatim_lines};
17use alloc::string::{String, ToString};
18use alloc::vec::Vec;
19use zerodds_lang_cst::common::{Doc, Ident, Literal};
20use zerodds_lang_cst::go::{
21 ConstDecl, Decl, Expr, Field, File, FuncDecl, Import, InterfaceDecl, Method, Param, Stmt, Tag,
22 Type, TypeDecl,
23};
24
25#[must_use]
27pub fn file(file: &File) -> String {
28 let mut w = Writer::new("\t");
29 doc(&mut w, &file.doc);
30 let mut package = String::from("package ");
31 package.push_str(file.package.as_str());
32 w.line(&package);
33 if !file.imports.is_empty() {
34 w.blank_line();
35 render_imports(&mut w, &file.imports);
36 }
37 for decl in &file.decls {
38 w.blank_line();
39 render_decl(&mut w, decl);
40 }
41 w.finish()
42}
43
44#[must_use]
46pub fn decl(node: &Decl) -> String {
47 let mut w = Writer::new("\t");
48 render_decl(&mut w, node);
49 w.finish()
50}
51
52#[must_use]
54pub fn type_(node: &Type) -> String {
55 type_text(node)
56}
57
58fn render_imports(w: &mut Writer, imports: &[Import]) {
61 if let [only] = imports {
62 let mut line = String::from("import ");
63 line.push_str(&import_text(only));
64 w.line(&line);
65 return;
66 }
67 w.line("import (");
68 w.indent();
69 for import in imports {
70 w.line(&import_text(import));
71 }
72 w.dedent();
73 w.line(")");
74}
75
76fn import_text(import: &Import) -> String {
77 let mut out = String::new();
78 if let Some(alias) = &import.alias {
79 out.push_str(alias.as_str());
80 out.push(' ');
81 }
82 out.push('"');
83 out.push_str(&escape_string(&import.path));
84 out.push('"');
85 out
86}
87
88fn render_decl(w: &mut Writer, node: &Decl) {
89 match node {
90 Decl::Type(decl) => render_type_decl(w, decl),
91 Decl::Const(decl) => render_const(w, decl),
92 Decl::Interface(decl) => render_interface(w, decl),
93 Decl::Func(decl) => render_func(w, decl),
94 Decl::Comment(lines) => doc(w, lines),
95 Decl::Verbatim(text) => verbatim_lines(w, text),
96 }
97}
98
99fn render_func(w: &mut Writer, node: &FuncDecl) {
101 doc(w, &node.doc);
102 let mut head = String::from("func ");
103 head.push_str(node.name.as_str());
104 head.push('(');
105 let params: Vec<String> = node.params.iter().map(param_text).collect();
106 head.push_str(¶ms.join(", "));
107 head.push(')');
108 match node.results.as_slice() {
109 [] => {}
110 [only] => {
111 head.push(' ');
112 head.push_str(&type_text(only));
113 }
114 many => {
115 head.push_str(" (");
116 let parts: Vec<String> = many.iter().map(type_text).collect();
117 head.push_str(&parts.join(", "));
118 head.push(')');
119 }
120 }
121 head.push_str(" {");
122 w.line(&head);
123 w.indent();
124 render_stmts(w, &node.body);
125 w.dedent();
126 w.line("}");
127}
128
129fn render_stmts(w: &mut Writer, body: &[Stmt]) {
136 for statement in body {
137 render_stmt(w, statement);
138 }
139}
140
141fn render_stmt(w: &mut Writer, node: &Stmt) {
148 match node {
149 Stmt::Var { name, type_, value } => {
150 let mut line;
151 match (type_, value) {
152 (None, Some(value)) => {
154 line = String::from(name.as_str());
155 line.push_str(" := ");
156 line.push_str(&expr_text(value));
157 }
158 (Some(type_), value) => {
159 line = String::from("var ");
160 line.push_str(name.as_str());
161 line.push(' ');
162 line.push_str(&type_text(type_));
163 if let Some(value) = value {
164 line.push_str(" = ");
165 line.push_str(&expr_text(value));
166 }
167 }
168 (None, None) => {
172 line = String::from("var ");
173 line.push_str(name.as_str());
174 }
175 }
176 w.line(&line);
177 }
178 Stmt::Assign { place, value } => {
179 let mut line = expr_text(place);
180 line.push_str(" = ");
181 line.push_str(&expr_text(value));
182 w.line(&line);
183 }
184 Stmt::Expr(value) => w.line(&expr_text(value)),
185 Stmt::Return(value) => match value {
186 Some(value) => {
187 let mut line = String::from("return ");
188 line.push_str(&expr_text(value));
189 w.line(&line);
190 }
191 None => w.line("return"),
192 },
193 Stmt::If {
194 condition,
195 then,
196 otherwise,
197 } => render_if(w, condition, then, otherwise.as_deref(), "if "),
198 Stmt::For {
199 counter,
200 init,
201 bound,
202 body,
203 } => {
204 let mut line = String::from("for ");
205 line.push_str(counter.as_str());
206 line.push_str(" := ");
207 line.push_str(&expr_text(init));
208 line.push_str("; ");
209 line.push_str(counter.as_str());
210 line.push_str(" < ");
211 line.push_str(&expr_text(bound));
212 line.push_str("; ");
213 line.push_str(counter.as_str());
214 line.push_str("++ {");
215 w.line(&line);
216 w.indent();
217 render_stmts(w, body);
218 w.dedent();
219 w.line("}");
220 }
221 Stmt::While { condition, body } => {
222 let mut line = String::from("for ");
223 line.push_str(&expr_text(condition));
224 line.push_str(" {");
225 w.line(&line);
226 w.indent();
227 render_stmts(w, body);
228 w.dedent();
229 w.line("}");
230 }
231 Stmt::Break => w.line("break"),
232 Stmt::Comment(lines) => doc(w, lines),
233 }
234}
235
236fn render_if(
253 w: &mut Writer,
254 condition: &Expr,
255 then: &[Stmt],
256 otherwise: Option<&[Stmt]>,
257 head: &str,
258) {
259 let mut condition = condition;
260 let mut then = then;
261 let mut otherwise = otherwise;
262 let mut head = head;
263 loop {
264 let mut line = String::from(head);
265 line.push_str(&expr_text(condition));
266 line.push_str(" {");
267 w.line(&line);
268 w.indent();
269 render_stmts(w, then);
270 w.dedent();
271 match otherwise {
272 None => {
273 w.line("}");
274 return;
275 }
276 Some(
277 [
278 Stmt::If {
279 condition: next_condition,
280 then: next_then,
281 otherwise: next_otherwise,
282 },
283 ],
284 ) => {
285 condition = next_condition;
286 then = next_then;
287 otherwise = next_otherwise.as_deref();
288 head = "} else if ";
289 }
290 Some(branch) => {
291 w.line("} else {");
292 w.indent();
293 render_stmts(w, branch);
294 w.dedent();
295 w.line("}");
296 return;
297 }
298 }
299 }
300}
301
302fn render_type_decl(w: &mut Writer, node: &TypeDecl) {
303 doc(w, &node.doc);
304 let mut head = String::from("type ");
305 head.push_str(node.name.as_str());
306 head.push(' ');
307 if node.alias {
308 head.push_str("= ");
309 }
310 match &node.type_ {
311 Type::Struct(fields) => {
312 head.push_str("struct {");
313 w.line(&head);
314 w.indent();
315 for text in &node.body_text.begin {
316 verbatim_lines(w, text);
317 }
318 render_fields(w, fields);
319 for text in &node.body_text.end {
320 verbatim_lines(w, text);
321 }
322 w.dedent();
323 w.line("}");
324 }
325 other => {
326 head.push_str(&type_text(other));
327 w.line(&head);
328 }
329 }
330}
331
332fn render_fields(w: &mut Writer, fields: &[Field]) {
356 let mut run: Vec<&Field> = Vec::new();
357 for field in fields {
358 if !field.doc.is_empty() && !run.is_empty() {
359 flush_run(w, &run);
360 run.clear();
361 }
362 run.push(field);
363 }
364 flush_run(w, &run);
365}
366
367fn flush_run(w: &mut Writer, run: &[&Field]) {
369 let name_width = run
370 .iter()
371 .map(|field| field.name.as_str().chars().count())
372 .max()
373 .unwrap_or(0);
374 let type_width = run
375 .iter()
376 .filter(|field| field.tag.is_some())
377 .map(|field| type_text(&field.type_).chars().count())
378 .max()
379 .unwrap_or(0);
380 for field in run {
381 render_field(w, field, name_width, type_width);
382 }
383}
384
385fn render_field(w: &mut Writer, node: &Field, name_width: usize, type_width: usize) {
386 doc(w, &node.doc);
387 let mut line = String::from(node.name.as_str());
388 pad_to(&mut line, name_width);
389 line.push(' ');
390 let type_ = type_text(&node.type_);
391 line.push_str(&type_);
392 if let Some(tag) = &node.tag {
393 pad_to(&mut line, name_width + 1 + type_width);
394 line.push(' ');
395 line.push_str(&tag_text(tag));
396 }
397 w.line(&line);
398}
399
400fn pad_to(line: &mut String, width: usize) {
406 while line.chars().count() < width {
407 line.push(' ');
408 }
409}
410
411fn tag_text(tag: &Tag) -> String {
421 let mut out = String::from("`");
422 for (index, (key, value)) in tag.entries.iter().enumerate() {
423 if index > 0 {
424 out.push(' ');
425 }
426 out.push_str(key);
427 out.push_str(":\"");
428 out.push_str(value);
429 out.push('"');
430 }
431 out.push('`');
432 out
433}
434
435fn render_const(w: &mut Writer, node: &ConstDecl) {
436 doc(w, &node.doc);
437 let mut line = String::from("const ");
438 line.push_str(node.name.as_str());
439 if let Some(type_) = &node.type_ {
440 line.push(' ');
441 line.push_str(&type_text(type_));
442 }
443 line.push_str(" = ");
444 line.push_str(&expr_text(&node.value));
445 w.line(&line);
446}
447
448fn render_interface(w: &mut Writer, node: &InterfaceDecl) {
449 doc(w, &node.doc);
450 let mut head = String::from("type ");
451 head.push_str(node.name.as_str());
452 head.push_str(" interface {");
453 w.line(&head);
454 w.indent();
455 for method in &node.methods {
456 render_method(w, method);
457 }
458 w.dedent();
459 w.line("}");
460}
461
462fn render_method(w: &mut Writer, node: &Method) {
463 doc(w, &node.doc);
464 let mut line = String::from(node.name.as_str());
465 line.push('(');
466 let params: Vec<String> = node.params.iter().map(param_text).collect();
467 line.push_str(¶ms.join(", "));
468 line.push(')');
469 match node.results.as_slice() {
470 [] => {}
471 [only] => {
472 line.push(' ');
473 line.push_str(&type_text(only));
474 }
475 many => {
476 line.push_str(" (");
477 let parts: Vec<String> = many.iter().map(type_text).collect();
478 line.push_str(&parts.join(", "));
479 line.push(')');
480 }
481 }
482 w.line(&line);
483}
484
485fn param_text(param: &Param) -> String {
486 let mut out = String::new();
487 if let Some(name) = ¶m.name {
488 out.push_str(name.as_str());
489 out.push(' ');
490 }
491 out.push_str(&type_text(¶m.type_));
492 out
493}
494
495fn struct_text(fields: &[Field]) -> String {
501 if fields.is_empty() {
502 return "struct{}".to_string();
503 }
504 let mut w = Writer::new("\t");
505 w.line("struct {");
506 w.indent();
507 render_fields(&mut w, fields);
508 w.dedent();
509 w.push("}");
510 w.finish().trim_end().to_string()
511}
512
513fn type_text(node: &Type) -> String {
517 match node {
518 Type::Primitive(primitive) => primitive.as_str().to_string(),
519 Type::Named(name) => name.as_str().to_string(),
520 Type::Slice(element) => {
521 let mut out = String::from("[]");
522 out.push_str(&type_text(element));
523 out
524 }
525 Type::Array { length, element } => {
526 let mut out = String::from("[");
527 out.push_str(&expr_text(length));
528 out.push(']');
529 out.push_str(&type_text(element));
530 out
531 }
532 Type::Pointer(target) => {
533 let mut out = String::from("*");
534 out.push_str(&type_text(target));
535 out
536 }
537 Type::Struct(fields) => struct_text(fields),
538 }
539}
540
541fn expr_text(node: &Expr) -> String {
548 match node {
549 Expr::Literal(literal) => literal_text(literal),
550 Expr::Name(name) => name.as_str().to_string(),
551 Expr::Binary { op, lhs, rhs } => {
552 let mut out = expr_text(lhs);
553 out.push(' ');
554 out.push_str(op);
555 out.push(' ');
556 out.push_str(&expr_text(rhs));
557 out
558 }
559 Expr::Convert { type_, value } => {
560 let mut out = type_text(type_);
561 out.push('(');
562 out.push_str(&expr_text(value));
563 out.push(')');
564 out
565 }
566 Expr::Paren(inner) => {
567 let mut out = String::from("(");
568 out.push_str(&expr_text(inner));
569 out.push(')');
570 out
571 }
572 Expr::Call { func, args } => {
573 let mut out = expr_text(func);
574 out.push('(');
575 let parts: Vec<String> = args.iter().map(expr_text).collect();
576 out.push_str(&parts.join(", "));
577 out.push(')');
578 out
579 }
580 Expr::Selector { value, name } => {
581 let mut out = expr_text(value);
582 out.push('.');
583 out.push_str(name.as_str());
584 out
585 }
586 Expr::Index { value, index } => {
587 let mut out = expr_text(value);
588 out.push('[');
589 out.push_str(&expr_text(index));
590 out.push(']');
591 out
592 }
593 Expr::SliceTo { value, high } => {
594 let mut out = expr_text(value);
595 out.push_str("[:");
596 out.push_str(&expr_text(high));
597 out.push(']');
598 out
599 }
600 Expr::Unary { op, value } => {
601 let mut out = String::from(*op);
602 out.push_str(&expr_text(value));
603 out
604 }
605 Expr::Composite { type_, elems } => {
606 let mut out = match type_ {
607 Some(type_) => type_text(type_),
608 None => String::new(),
609 };
610 out.push('{');
611 let parts: Vec<String> = elems
612 .iter()
613 .map(|elem| match &elem.key {
614 Some(key) => {
615 let mut part = String::from(key.as_str());
616 part.push_str(": ");
617 part.push_str(&expr_text(&elem.value));
618 part
619 }
620 None => expr_text(&elem.value),
621 })
622 .collect();
623 out.push_str(&parts.join(", "));
624 out.push('}');
625 out
626 }
627 Expr::Make { type_, length } => {
628 let mut out = String::from("make(");
629 out.push_str(&type_text(type_));
630 out.push_str(", ");
631 out.push_str(&expr_text(length));
632 out.push(')');
633 out
634 }
635 }
636}
637
638fn literal_text(literal: &Literal) -> String {
639 match literal {
640 Literal::Int(text) | Literal::Float(text) => text.clone(),
641 Literal::Bool(value) => {
642 if *value {
643 "true".to_string()
644 } else {
645 "false".to_string()
646 }
647 }
648 Literal::Char(ch) => alloc::format!("{}", u32::from(*ch)),
656 Literal::Str(text) => {
657 let mut out = String::from("\"");
658 out.push_str(&escape_string(text));
659 out.push('"');
660 out
661 }
662 Literal::Bytes(bytes) => {
663 let mut out = String::from("[]byte{");
664 let parts: Vec<String> = bytes.iter().map(|b| alloc::format!("0x{b:02X}")).collect();
665 out.push_str(&parts.join(", "));
666 out.push('}');
667 out
668 }
669 Literal::Null => "nil".to_string(),
670 }
671}
672
673fn doc(w: &mut Writer, doc: &Doc) {
674 if doc.is_empty() {
675 return;
676 }
677 comment_lines(w, "// ", doc.as_lines());
678}
679
680#[must_use]
682pub fn ident(name: &Ident) -> String {
683 name.as_str().to_string()
684}