Skip to main content

kain/codegen/
js.rs

1//! JavaScript Code Generation - Transpiles KAIN AST to JavaScript
2//!
3//! This module generates clean, modern JavaScript (ES6+) from a typed KAIN program.
4//! The generated JS can run directly in browsers or Node.js.
5//!
6//! Key features:
7//! - JSX → DOM manipulation
8//! - Components → Functions returning DOM nodes
9//! - Clean, readable output
10//! - No runtime dependencies
11
12use crate::types::{TypedProgram, TypedItem};
13use crate::error::{KainResult, KainError};
14use crate::ast::{
15    Type, Expr, Stmt, Block, BinaryOp, UnaryOp, Pattern, Function, Struct, Enum,
16    Field, Variant, VariantFields, Impl, Param, MatchArm, CallArg, ElseBranch,
17    VariantPatternFields, EnumVariantFields, Component, JSXNode, JSXAttribute, JSXAttrValue,
18};
19use crate::span::Span;
20
21/// Generate JavaScript source code from a typed program
22pub fn generate(program: &TypedProgram) -> KainResult<String> {
23    let mut gen = JSGen::new();
24    Ok(gen.gen_program(program))
25}
26
27// StringBuilder helper for accumulated output
28struct StringBuilder {
29    lines: Vec<String>,
30}
31
32impl StringBuilder {
33    fn new() -> Self {
34        Self { lines: Vec::new() }
35    }
36
37    fn push(&mut self, text: &str) {
38        self.lines.push(text.to_string());
39    }
40
41    fn push_line(&mut self, text: &str) {
42        self.lines.push(format!("{}\n", text));
43    }
44
45    fn build(&self) -> String {
46        self.lines.join("")
47    }
48}
49
50// Main JavaScript code generator
51struct JSGen {
52    output: StringBuilder,
53    indent: usize,
54}
55
56impl JSGen {
57    fn new() -> Self {
58        Self {
59            output: StringBuilder::new(),
60            indent: 0,
61        }
62    }
63
64    fn indent(&mut self) {
65        self.indent += 1;
66    }
67
68    fn dedent(&mut self) {
69        if self.indent > 0 {
70            self.indent -= 1;
71        }
72    }
73
74    fn write(&mut self, text: &str) {
75        self.output.push(text);
76    }
77
78    fn writeln(&mut self, text: &str) {
79        let indent_str = "  ".repeat(self.indent);
80        self.output.push_line(&format!("{}{}", indent_str, text));
81    }
82
83    fn gen_program(&mut self, program: &TypedProgram) -> String {
84        // Header comment
85        self.writeln("// Generated by KAIN compiler");
86        self.writeln("// Target: JavaScript (ES6+)");
87        self.writeln("");
88
89        // Generate all items
90        for item in &program.items {
91            match item {
92                TypedItem::Function(f) => self.gen_function(&f.ast),
93                TypedItem::Struct(s) => self.gen_struct(&s.ast),
94                TypedItem::Enum(e) => self.gen_enum(&e.ast),
95                TypedItem::Component(c) => self.gen_component(&c.ast),
96                TypedItem::Const(c) => self.gen_const(&c.ast.name, &c.ast.value),
97                TypedItem::Impl(i) => self.gen_impl(&i.ast),
98                _ => {} // Skip other items for now
99            }
100            self.writeln("");
101        }
102
103        self.output.build()
104    }
105
106    fn gen_function(&mut self, func: &Function) {
107        // Function signature
108        let params = func.params.iter()
109            .map(|p| p.name.clone())
110            .collect::<Vec<_>>()
111            .join(", ");
112
113        self.writeln(&format!("function {}({}) {{", func.name, params));
114        self.indent();
115
116        // Function body
117        self.gen_block(&func.body);
118
119        self.dedent();
120        self.writeln("}");
121    }
122
123    fn gen_struct(&mut self, s: &Struct) {
124        // Generate as a class
125        self.writeln(&format!("class {} {{", s.name));
126        self.indent();
127
128        // Constructor
129        let params = s.fields.iter()
130            .map(|f| f.name.clone())
131            .collect::<Vec<_>>()
132            .join(", ");
133
134        self.writeln(&format!("constructor({}) {{", params));
135        self.indent();
136        for field in &s.fields {
137            self.writeln(&format!("this.{} = {};", field.name, field.name));
138        }
139        self.dedent();
140        self.writeln("}");
141
142        self.dedent();
143        self.writeln("}");
144    }
145
146    fn gen_enum(&mut self, e: &Enum) {
147        // Generate enum as object with variant constructors
148        self.writeln(&format!("const {} = {{", e.name));
149        self.indent();
150
151        for variant in &e.variants {
152            match &variant.fields {
153                VariantFields::Unit => {
154                    self.writeln(&format!("{}: {{ type: '{}', tag: '{}' }},", 
155                        variant.name, e.name, variant.name));
156                }
157                VariantFields::Tuple(types) => {
158                    let params = (0..types.len())
159                        .map(|i| format!("_{}", i))
160                        .collect::<Vec<_>>()
161                        .join(", ");
162                    
163                    self.writeln(&format!("{}: ({}) => ({{", variant.name, params));
164                    self.indent();
165                    self.writeln(&format!("type: '{}',", e.name));
166                    self.writeln(&format!("tag: '{}',", variant.name));
167                    for i in 0..types.len() {
168                        self.writeln(&format!("_{}: _{},", i, i));
169                    }
170                    self.dedent();
171                    self.writeln("}),");
172                }
173                VariantFields::Struct(fields) => {
174                    let params = fields.iter()
175                        .map(|f| f.name.clone())
176                        .collect::<Vec<_>>()
177                        .join(", ");
178                    
179                    self.writeln(&format!("{}: ({}) => ({{", variant.name, params));
180                    self.indent();
181                    self.writeln(&format!("type: '{}',", e.name));
182                    self.writeln(&format!("tag: '{}',", variant.name));
183                    for field in fields {
184                        self.writeln(&format!("{},", field.name));
185                    }
186                    self.dedent();
187                    self.writeln("}),");
188                }
189            }
190        }
191
192        self.dedent();
193        self.writeln("};");
194    }
195
196    fn gen_component(&mut self, comp: &Component) {
197        // Component as function returning DOM node
198        let params = comp.props.iter()
199            .map(|p| p.name.clone())
200            .collect::<Vec<_>>()
201            .join(", ");
202
203        self.writeln(&format!("function {}({}) {{", comp.name, params));
204        self.indent();
205
206        // State initialization
207        for state in &comp.state {
208            self.write(&format!("let {} = ", state.name));
209            self.gen_expr(&state.initial);
210            self.writeln(";");
211        }
212
213        // Methods
214        for method in &comp.methods {
215            self.gen_function(method);
216        }
217
218        // Render JSX to DOM
219        self.write("return ");
220        self.gen_jsx(&comp.body);
221        self.writeln(";");
222
223        self.dedent();
224        self.writeln("}");
225    }
226
227    fn gen_const(&mut self, name: &str, value: &Expr) {
228        self.write(&format!("const {} = ", name));
229        self.gen_expr(value);
230        self.writeln(";");
231    }
232
233    fn gen_impl(&mut self, impl_block: &Impl) {
234        // Generate methods as static or prototype methods
235        if let Type::Named { name, .. } = &impl_block.target_type {
236            for method in &impl_block.methods {
237                let params = method.params.iter()
238                    .skip(if method.params.first().map(|p| p.name == "self").unwrap_or(false) { 1 } else { 0 })
239                    .map(|p| p.name.clone())
240                    .collect::<Vec<_>>()
241                    .join(", ");
242
243                let has_self = method.params.first().map(|p| p.name == "self").unwrap_or(false);
244                
245                if has_self {
246                    // Instance method
247                    self.writeln(&format!("{}.prototype.{} = function({}) {{", 
248                        name, method.name, params));
249                } else {
250                    // Static method
251                    self.writeln(&format!("{}.{} = function({}) {{", 
252                        name, method.name, params));
253                }
254                
255                self.indent();
256                self.gen_block(&method.body);
257                self.dedent();
258                self.writeln("};");
259            }
260        }
261    }
262
263    fn gen_block(&mut self, block: &Block) {
264        for stmt in &block.stmts {
265            self.gen_stmt(stmt);
266        }
267    }
268
269    fn gen_stmt(&mut self, stmt: &Stmt) {
270        match stmt {
271            Stmt::Expr(expr) => {
272                self.gen_expr(expr);
273                self.writeln(";");
274            }
275            Stmt::Let { pattern, value, .. } => {
276                if let Pattern::Binding { name, .. } = pattern {
277                    self.write(&format!("let {} = ", name));
278                    if let Some(val) = value {
279                        self.gen_expr(val);
280                    } else {
281                        self.write("null");
282                    }
283                    self.writeln(";");
284                }
285            }
286            Stmt::Return(expr, _) => {
287                self.write("return");
288                if let Some(e) = expr {
289                    self.write(" ");
290                    self.gen_expr(e);
291                }
292                self.writeln(";");
293            }
294            Stmt::For { binding, iter, body, .. } => {
295                if let Pattern::Binding { name, .. } = binding {
296                    self.write(&format!("for (const {} of ", name));
297                    self.gen_expr(iter);
298                    self.writeln(") {");
299                    self.indent();
300                    self.gen_block(body);
301                    self.dedent();
302                    self.writeln("}");
303                }
304            }
305            Stmt::While { condition, body, .. } => {
306                self.write("while (");
307                self.gen_expr(condition);
308                self.writeln(") {");
309                self.indent();
310                self.gen_block(body);
311                self.dedent();
312                self.writeln("}");
313            }
314            Stmt::Loop { body, .. } => {
315                self.writeln("while (true) {");
316                self.indent();
317                self.gen_block(body);
318                self.dedent();
319                self.writeln("}");
320            }
321            Stmt::Break(expr, _) => {
322                if expr.is_some() {
323                    self.writeln("// Note: break with value not supported in JS");
324                }
325                self.writeln("break;");
326            }
327            Stmt::Continue(_) => {
328                self.writeln("continue;");
329            }
330            _ => {
331                self.writeln("// Unsupported statement");
332            }
333        }
334    }
335
336    fn gen_expr(&mut self, expr: &Expr) {
337        match expr {
338            Expr::Int(n, _) => self.write(&n.to_string()),
339            Expr::Float(f, _) => self.write(&f.to_string()),
340            Expr::String(s, _) => self.write(&format!("\"{}\"", s.escape_default())),
341            Expr::Bool(b, _) => self.write(if *b { "true" } else { "false" }),
342            Expr::None(_) => self.write("null"),
343            Expr::Ident(name, _) => self.write(name),
344            
345            Expr::Binary { left, op, right, .. } => {
346                self.write("(");
347                self.gen_expr(left);
348                self.write(&format!(" {} ", self.gen_binop(*op)));
349                self.gen_expr(right);
350                self.write(")");
351            }
352            
353            Expr::Unary { op, operand, .. } => {
354                self.write(&format!("({}",
355 self.gen_unop(*op)));
356                self.gen_expr(operand);
357                self.write(")");
358            }
359            
360            Expr::Call { callee, args, .. } => {
361                self.gen_expr(callee);
362                self.write("(");
363                for (i, arg) in args.iter().enumerate() {
364                    if i > 0 {
365                        self.write(", ");
366                    }
367                    self.gen_expr(&arg.value);
368                }
369                self.write(")");
370            }
371            
372            Expr::Array(elements, _) => {
373                self.write("[");
374                for (i, elem) in elements.iter().enumerate() {
375                    if i > 0 {
376                        self.write(", ");
377                    }
378                    self.gen_expr(elem);
379                }
380                self.write("]");
381            }
382            
383            Expr::Tuple(elements, _) => {
384                // Tuples as arrays in JS
385                self.write("[");
386                for (i, elem) in elements.iter().enumerate() {
387                    if i > 0 {
388                        self.write(", ");
389                    }
390                    self.gen_expr(elem);
391                }
392                self.write("]");
393            }
394            
395            Expr::Index { object, index, .. } => {
396                self.gen_expr(object);
397                self.write("[");
398                self.gen_expr(index);
399                self.write("]");
400            }
401            
402            Expr::Field { object, field, .. } => {
403                self.gen_expr(object);
404                self.write(&format!(".{}", field));
405            }
406            
407            Expr::MethodCall { receiver, method, args, .. } => {
408                self.gen_expr(receiver);
409                self.write(&format!(".{}(", method));
410                for (i, arg) in args.iter().enumerate() {
411                    if i > 0 {
412                        self.write(", ");
413                    }
414                    self.gen_expr(&arg.value);
415                }
416                self.write(")");
417            }
418            
419            Expr::Struct { name, fields, .. } => {
420                self.write(&format!("new {}(", name));
421                for (i, (_, expr)) in fields.iter().enumerate() {
422                    if i > 0 {
423                        self.write(", ");
424                    }
425                    self.gen_expr(expr);
426                }
427                self.write(")");
428            }
429            
430            Expr::If { condition, then_branch, else_branch, .. } => {
431                self.write("(");
432                self.gen_expr(condition);
433                self.write(" ? ");
434                self.gen_block_as_expr(then_branch);
435                self.write(" : ");
436                if let Some(else_br) = else_branch {
437                    match else_br.as_ref() {
438                        ElseBranch::Else(else_block) => self.gen_block_as_expr(else_block),
439                        ElseBranch::ElseIf { .. } => {
440                            // Nested if-else
441                            self.write("/* else-if */null");
442                        }
443                    }
444                } else {
445                    self.write("null");
446                }
447                self.write(")");
448            }
449            
450            Expr::Match { scrutinee, arms, .. } => {
451                // Generate as IIFE with switch/if-else
452                self.write("(() => {");
453                self.indent();
454                self.writeln("");
455                self.write("const __match = ");
456                self.gen_expr(scrutinee);
457                self.writeln(";");
458                
459                for (i, arm) in arms.iter().enumerate() {
460                    if i == 0 {
461                        self.write("if (");
462                    } else {
463                        self.write("else if (");
464                    }
465                    self.gen_pattern_match("__match", &arm.pattern);
466                    self.writeln(") {");
467                    self.indent();
468                    self.write("return ");
469                    self.gen_expr(&arm.body);
470                    self.writeln(";");
471                    self.dedent();
472                    self.write("} ");
473                }
474                self.writeln("");
475                self.writeln("throw new Error('Non-exhaustive match');");
476                self.dedent();
477                self.write("})()");
478            }
479            
480            Expr::Lambda { params, body, .. } => {
481                let param_names = params.iter()
482                    .map(|p| p.name.clone())
483                    .collect::<Vec<_>>()
484                    .join(", ");
485                self.write(&format!("({}) => ", param_names));
486                self.gen_expr(body);
487            }
488            
489            Expr::Block(block, _) => {
490                self.write("(() => {");
491                self.indent();
492                self.writeln("");
493                self.gen_block(block);
494                self.dedent();
495                self.write("})()");
496            }
497            
498            Expr::Assign { target, value, .. } => {
499                self.gen_expr(target);
500                self.write(" = ");
501                self.gen_expr(value);
502            }
503            
504            Expr::FString(parts, _) => {
505                self.write("`");
506                for part in parts {
507                    match part {
508                        Expr::String(s, _) => self.write(s),
509                        _ => {
510                            self.write("${");
511                            self.gen_expr(part);
512                            self.write("}");
513                        }
514                    }
515                }
516                self.write("`");
517            }
518            
519            Expr::JSX(node, _) => {
520                self.gen_jsx(node);
521            }
522            
523            Expr::EnumVariant { enum_name, variant, fields, .. } => {
524                self.write(&format!("{}.{}(", enum_name, variant));
525                match fields {
526                    EnumVariantFields::Unit => {}
527                    EnumVariantFields::Tuple(exprs) => {
528                        for (i, expr) in exprs.iter().enumerate() {
529                            if i > 0 {
530                                self.write(", ");
531                            }
532                            self.gen_expr(expr);
533                        }
534                    }
535                    EnumVariantFields::Struct(named) => {
536                        for (i, (_, expr)) in named.iter().enumerate() {
537                            if i > 0 {
538                                self.write(", ");
539                            }
540                            self.gen_expr(expr);
541                        }
542                    }
543                }
544                self.write(")");
545            }
546            
547            _ => {
548                self.write("/* unsupported expr */");
549            }
550        }
551    }
552
553    fn gen_jsx(&mut self, node: &JSXNode) {
554        match node {
555            JSXNode::Element { tag, attributes, children, .. } => {
556                // Create element
557                self.write(&format!("(() => {{"));
558                self.indent();
559                self.writeln("");
560                self.writeln(&format!("const __el = document.createElement('{}');", tag));
561                
562                // Set attributes
563                for attr in attributes {
564                    match &attr.value {
565                        JSXAttrValue::String(s) => {
566                            if attr.name == "class" {
567                                self.writeln(&format!("__el.className = '{}';", s));
568                            } else {
569                                self.writeln(&format!("__el.setAttribute('{}', '{}');", attr.name, s));
570                            }
571                        }
572                        JSXAttrValue::Bool(b) => {
573                            if *b {
574                                self.writeln(&format!("__el.setAttribute('{}', '');", attr.name));
575                            }
576                        }
577                        JSXAttrValue::Expr(expr) => {
578                            if attr.name.starts_with("on_") {
579                                // Event handler
580                                let event = attr.name.trim_start_matches("on_");
581                                self.write(&format!("__el.addEventListener('{}', ", event));
582                                self.gen_expr(expr);
583                                self.writeln(");");
584                            } else if attr.name == "class" {
585                                self.write("__el.className = ");
586                                self.gen_expr(expr);
587                                self.writeln(";");
588                            } else {
589                                self.write(&format!("__el.setAttribute('{}', ", attr.name));
590                                self.gen_expr(expr);
591                                self.writeln(");");
592                            }
593                        }
594                    }
595                }
596                
597                // Append children
598                for child in children {
599                    self.write("__el.appendChild(");
600                    self.gen_jsx(child);
601                    self.writeln(");");
602                }
603                
604                self.writeln("return __el;");
605                self.dedent();
606                self.write("})()");
607            }
608            
609            JSXNode::Text(text, _) => {
610                self.write(&format!("document.createTextNode('{}')", text.escape_default()));
611            }
612            
613            JSXNode::Expression(expr) => {
614                self.write("(() => {");
615                self.indent();
616                self.writeln("");
617                self.write("const __val = ");
618                self.gen_expr(expr);
619                self.writeln(";");
620                self.writeln("if (typeof __val === 'string' || typeof __val === 'number') {");
621                self.indent();
622                self.writeln("return document.createTextNode(String(__val));");
623                self.dedent();
624                self.writeln("} else if (__val instanceof Node) {");
625                self.indent();
626                self.writeln("return __val;");
627                self.dedent();
628                self.writeln("} else {");
629                self.indent();
630                self.writeln("return document.createTextNode('');");
631                self.dedent();
632                self.writeln("}");
633                self.dedent();
634                self.write("})()");
635            }
636            
637            JSXNode::ComponentCall { name, props, children, .. } => {
638                self.write(&format!("{}({{", name));
639                for (i, prop) in props.iter().enumerate() {
640                    if i > 0 {
641                        self.write(", ");
642                    }
643                    self.write(&format!("{}: ", prop.name));
644                    match &prop.value {
645                        JSXAttrValue::String(s) => self.write(&format!("'{}'", s)),
646                        JSXAttrValue::Bool(b) => self.write(&b.to_string()),
647                        JSXAttrValue::Expr(e) => self.gen_expr(e),
648                    }
649                }
650                if !children.is_empty() {
651                    if !props.is_empty() {
652                        self.write(", ");
653                    }
654                    self.write("children: [");
655                    for (i, child) in children.iter().enumerate() {
656                        if i > 0 {
657                            self.write(", ");
658                        }
659                        self.gen_jsx(child);
660                    }
661                    self.write("]");
662                }
663                self.write("})");
664            }
665            
666            JSXNode::Fragment(children, _) => {
667                self.write("(() => {");
668                self.indent();
669                self.writeln("");
670                self.writeln("const __frag = document.createDocumentFragment();");
671                for child in children {
672                    self.write("__frag.appendChild(");
673                    self.gen_jsx(child);
674                    self.writeln(");");
675                }
676                self.writeln("return __frag;");
677                self.dedent();
678                self.write("})()");
679            }
680            
681            JSXNode::For { binding, iter, body, .. } => {
682                self.write("(() => {");
683                self.indent();
684                self.writeln("");
685                self.writeln("const __frag = document.createDocumentFragment();");
686                self.write(&format!("for (const {} of ", binding));
687                self.gen_expr(iter);
688                self.writeln(") {");
689                self.indent();
690                self.write("__frag.appendChild(");
691                self.gen_jsx(body);
692                self.writeln(");");
693                self.dedent();
694                self.writeln("}");
695                self.writeln("return __frag;");
696                self.dedent();
697                self.write("})()");
698            }
699            
700            JSXNode::If { condition, then_branch, else_branch, .. } => {
701                self.write("(() => {");
702                self.indent();
703                self.writeln("");
704                self.write("if (");
705                self.gen_expr(condition);
706                self.writeln(") {");
707                self.indent();
708                self.write("return ");
709                self.gen_jsx(then_branch);
710                self.writeln(";");
711                self.dedent();
712                if let Some(else_node) = else_branch {
713                    self.writeln("} else {");
714                    self.indent();
715                    self.write("return ");
716                    self.gen_jsx(else_node);
717                    self.writeln(";");
718                    self.dedent();
719                }
720                self.writeln("}");
721                self.writeln("return document.createTextNode('');");
722                self.dedent();
723                self.write("})()");
724            }
725        }
726    }
727
728    fn gen_block_as_expr(&mut self, block: &Block) {
729        self.write("(() => {");
730        self.indent();
731        self.writeln("");
732        self.gen_block(block);
733        self.dedent();
734        self.write("})()");
735    }
736
737    fn gen_pattern_match(&mut self, scrutinee: &str, pattern: &Pattern) {
738        match pattern {
739            Pattern::Wildcard(_) => self.write("true"),
740            Pattern::Binding { .. } => self.write("true"),
741            Pattern::Literal(expr) => {
742                self.write(&format!("{} === ", scrutinee));
743                self.gen_expr(expr);
744            }
745            Pattern::Variant { enum_name, variant, fields, .. } => {
746                if let Some(enum_name) = enum_name {
747                    self.write(&format!("{}.type === '{}' && {}.tag === '{}'", 
748                        scrutinee, enum_name, scrutinee, variant));
749                } else {
750                    self.write(&format!("{}.tag === '{}'", scrutinee, variant));
751                }
752                
753                // Check fields if needed
754                match fields {
755                    VariantPatternFields::Tuple(patterns) => {
756                        for (i, _) in patterns.iter().enumerate() {
757                            self.write(&format!(" && typeof {}._{} !== 'undefined'", scrutinee, i));
758                        }
759                    }
760                    _ => {}
761                }
762            }
763            _ => self.write("false"),
764        }
765    }
766
767    fn gen_binop(&self, op: BinaryOp) -> &'static str {
768        match op {
769            BinaryOp::Add => "+",
770            BinaryOp::Sub => "-",
771            BinaryOp::Mul => "*",
772            BinaryOp::Div => "/",
773            BinaryOp::Mod => "%",
774            BinaryOp::Pow => "**",
775            BinaryOp::Eq => "===",
776            BinaryOp::Ne => "!==",
777            BinaryOp::Lt => "<",
778            BinaryOp::Le => "<=",
779            BinaryOp::Gt => ">",
780            BinaryOp::Ge => ">=",
781            BinaryOp::And => "&&",
782            BinaryOp::Or => "||",
783            BinaryOp::BitAnd => "&",
784            BinaryOp::BitOr => "|",
785            BinaryOp::BitXor => "^",
786            BinaryOp::Shl => "<<",
787            BinaryOp::Shr => ">>",
788            BinaryOp::Assign => "=",
789            BinaryOp::AddAssign => "+=",
790            BinaryOp::SubAssign => "-=",
791            BinaryOp::MulAssign => "*=",
792            BinaryOp::DivAssign => "/=",
793            BinaryOp::Range | BinaryOp::RangeInclusive => {
794                // Ranges need special handling, not a simple operator
795                "/* range */"
796            }
797        }
798    }
799
800    fn gen_unop(&self, op: UnaryOp) -> &'static str {
801        match op {
802            UnaryOp::Neg => "-",
803            UnaryOp::Not => "!",
804            UnaryOp::BitNot => "~",
805            UnaryOp::Ref | UnaryOp::RefMut | UnaryOp::Deref => {
806                // JS doesn't have these concepts, just pass through
807                ""
808            }
809        }
810    }
811}