1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::DART_KEYWORDS;
9use super::functions::*;
10
11#[derive(Debug, Clone, PartialEq)]
13pub enum DartExpr {
14 Lit(DartLit),
16 Var(String),
18 Field(Box<DartExpr>, String),
20 MethodCall(Box<DartExpr>, String, Vec<DartExpr>),
22 Call(Box<DartExpr>, Vec<DartExpr>),
24 New(String, Option<String>, Vec<DartExpr>),
26 ListLit(Vec<DartExpr>),
28 MapLit(Vec<(DartExpr, DartExpr)>),
30 SetLit(Vec<DartExpr>),
32 Lambda(Vec<(DartType, String)>, Box<DartExpr>),
34 Arrow(Vec<(DartType, String)>, Box<DartExpr>),
36 BinOp(Box<DartExpr>, String, Box<DartExpr>),
38 UnaryOp(String, Box<DartExpr>),
40 Ternary(Box<DartExpr>, Box<DartExpr>, Box<DartExpr>),
42 NullAware(Box<DartExpr>, String),
44 NullCoalesce(Box<DartExpr>, Box<DartExpr>),
46 Cascade(Box<DartExpr>, String, Vec<DartExpr>),
48 Await(Box<DartExpr>),
50 As(Box<DartExpr>, DartType),
52 Is(Box<DartExpr>, DartType),
54 Throw(Box<DartExpr>),
56 Spread(Box<DartExpr>),
58 Index(Box<DartExpr>, Box<DartExpr>),
60 Raw(String),
62}
63#[allow(dead_code)]
65#[derive(Debug, Clone)]
66pub struct DartEnum {
67 pub name: String,
68 pub variants: Vec<DartEnumVariant>,
69 pub implements: Vec<DartType>,
70 pub doc: Option<String>,
71}
72#[allow(dead_code)]
73impl DartEnum {
74 pub fn new(name: impl Into<String>) -> Self {
75 DartEnum {
76 name: name.into(),
77 variants: vec![],
78 implements: vec![],
79 doc: None,
80 }
81 }
82 pub fn add_variant(&mut self, v: DartEnumVariant) {
83 self.variants.push(v);
84 }
85 pub fn emit(&self) -> String {
86 let mut out = String::new();
87 if let Some(doc) = &self.doc {
88 out.push_str(&format!("/// {}\n", doc));
89 }
90 let impl_part = if self.implements.is_empty() {
91 String::new()
92 } else {
93 let impls: Vec<String> = self.implements.iter().map(|t| t.to_string()).collect();
94 format!(" implements {}", impls.join(", "))
95 };
96 out.push_str(&format!("enum {}{} {{\n", self.name, impl_part));
97 for (i, variant) in self.variants.iter().enumerate() {
98 let comma = if i + 1 < self.variants.len() {
99 ","
100 } else {
101 ";"
102 };
103 if let Some(doc) = &variant.doc {
104 out.push_str(&format!(" /// {}\n", doc));
105 }
106 out.push_str(&format!(" {}{}\n", variant.name, comma));
107 }
108 out.push_str("}\n");
109 out
110 }
111}
112#[derive(Debug, Clone, PartialEq)]
114pub enum DartLit {
115 Int(i64),
117 Double(f64),
119 Bool(bool),
121 Str(String),
123 Null,
125}
126#[derive(Debug, Clone)]
128pub struct DartImport {
129 pub uri: String,
130 pub as_prefix: Option<String>,
131 pub show: Vec<String>,
132 pub hide: Vec<String>,
133 pub is_deferred: bool,
134}
135impl DartImport {
136 pub fn simple(uri: impl Into<String>) -> Self {
137 DartImport {
138 uri: uri.into(),
139 as_prefix: None,
140 show: Vec::new(),
141 hide: Vec::new(),
142 is_deferred: false,
143 }
144 }
145 pub fn with_prefix(uri: impl Into<String>, prefix: impl Into<String>) -> Self {
146 DartImport {
147 uri: uri.into(),
148 as_prefix: Some(prefix.into()),
149 show: Vec::new(),
150 hide: Vec::new(),
151 is_deferred: false,
152 }
153 }
154 pub fn emit(&self) -> String {
156 let mut out = format!("import '{}'", self.uri);
157 if self.is_deferred {
158 out.push_str(" deferred");
159 }
160 if let Some(prefix) = &self.as_prefix {
161 out.push_str(&format!(" as {}", prefix));
162 }
163 if !self.show.is_empty() {
164 out.push_str(&format!(" show {}", self.show.join(", ")));
165 }
166 if !self.hide.is_empty() {
167 out.push_str(&format!(" hide {}", self.hide.join(", ")));
168 }
169 out.push_str(";\n");
170 out
171 }
172}
173#[derive(Debug, Clone)]
175pub struct DartClass {
176 pub name: String,
177 pub type_params: Vec<String>,
178 pub extends: Option<String>,
179 pub implements: Vec<String>,
180 pub mixins: Vec<String>,
181 pub fields: Vec<DartField>,
182 pub constructors: Vec<DartFunction>,
183 pub methods: Vec<DartFunction>,
184 pub is_abstract: bool,
185 pub doc: Option<String>,
186}
187impl DartClass {
188 pub fn new(name: impl Into<String>) -> Self {
189 DartClass {
190 name: name.into(),
191 type_params: Vec::new(),
192 extends: None,
193 implements: Vec::new(),
194 mixins: Vec::new(),
195 fields: Vec::new(),
196 constructors: Vec::new(),
197 methods: Vec::new(),
198 is_abstract: false,
199 doc: None,
200 }
201 }
202}
203#[allow(dead_code)]
205#[derive(Debug, Clone)]
206pub struct DartExtension {
207 pub name: Option<String>,
208 pub on_type: DartType,
209 pub methods: Vec<DartFunction>,
210 pub getters: Vec<DartFunction>,
211}
212#[allow(dead_code)]
213impl DartExtension {
214 pub fn new(on_type: DartType) -> Self {
215 DartExtension {
216 name: None,
217 on_type,
218 methods: vec![],
219 getters: vec![],
220 }
221 }
222 pub fn named(mut self, name: impl Into<String>) -> Self {
223 self.name = Some(name.into());
224 self
225 }
226 pub fn add_method(&mut self, m: DartFunction) {
227 self.methods.push(m);
228 }
229 pub fn add_getter(&mut self, g: DartFunction) {
230 self.getters.push(g);
231 }
232 pub fn emit(&self, backend: &DartBackend, indent_level: usize) -> String {
233 let name_part = self
234 .name
235 .as_deref()
236 .map(|n| format!(" {}", n))
237 .unwrap_or_default();
238 let pad = " ".repeat(indent_level);
239 let mut out = format!("{}extension{} on {} {{\n", pad, name_part, self.on_type);
240 for g in &self.getters {
241 out.push_str(&backend.emit_getter(g, indent_level + 1));
242 }
243 for m in &self.methods {
244 out.push_str(&backend.emit_function(m, indent_level + 1));
245 }
246 out.push_str(&format!("{}}}\n", pad));
247 out
248 }
249}
250#[allow(dead_code)]
252#[derive(Debug, Clone)]
253pub struct DartEnumVariant {
254 pub name: String,
255 pub values: Vec<DartExpr>,
256 pub doc: Option<String>,
257}
258#[allow(dead_code)]
259impl DartEnumVariant {
260 pub fn simple(name: impl Into<String>) -> Self {
261 DartEnumVariant {
262 name: name.into(),
263 values: vec![],
264 doc: None,
265 }
266 }
267}
268#[allow(dead_code)]
270#[derive(Debug, Clone)]
271pub struct DartMixin {
272 pub name: String,
273 pub on_types: Vec<DartType>,
274 pub fields: Vec<DartField>,
275 pub methods: Vec<DartFunction>,
276 pub doc: Option<String>,
277}
278#[allow(dead_code)]
279impl DartMixin {
280 pub fn new(name: impl Into<String>) -> Self {
281 DartMixin {
282 name: name.into(),
283 on_types: vec![],
284 fields: vec![],
285 methods: vec![],
286 doc: None,
287 }
288 }
289 pub fn with_on(mut self, ty: DartType) -> Self {
290 self.on_types.push(ty);
291 self
292 }
293 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
294 self.doc = Some(doc.into());
295 self
296 }
297 pub fn emit(&self, backend: &DartBackend, indent_level: usize) -> String {
298 let pad = " ".repeat(indent_level);
299 let mut out = String::new();
300 if let Some(doc) = &self.doc {
301 out.push_str(&format!("{}/// {}\n", pad, doc));
302 }
303 let on_part = if self.on_types.is_empty() {
304 String::new()
305 } else {
306 let types: Vec<String> = self.on_types.iter().map(|t| t.to_string()).collect();
307 format!(" on {}", types.join(", "))
308 };
309 out.push_str(&format!("{}mixin {}{} {{\n", pad, self.name, on_part));
310 for field in &self.fields {
311 out.push_str(&format!(
312 "{}{}\n",
313 " ".repeat(indent_level + 1),
314 emit_dart_field(field)
315 ));
316 }
317 for method in &self.methods {
318 out.push_str(&backend.emit_function(method, indent_level + 1));
319 }
320 out.push_str(&format!("{}}}\n", pad));
321 out
322 }
323}
324#[derive(Debug, Clone)]
326pub struct DartModule {
327 pub imports: Vec<DartImport>,
328 pub exports: Vec<String>,
329 pub classes: Vec<DartClass>,
330 pub functions: Vec<DartFunction>,
331 pub globals: Vec<(DartType, String, DartExpr)>,
332 pub part_of: Option<String>,
333}
334impl DartModule {
335 pub fn new() -> Self {
336 DartModule {
337 imports: Vec::new(),
338 exports: Vec::new(),
339 classes: Vec::new(),
340 functions: Vec::new(),
341 globals: Vec::new(),
342 part_of: None,
343 }
344 }
345}
346#[derive(Debug, Clone)]
348pub struct DartFunction {
349 pub name: String,
350 pub return_type: DartType,
351 pub params: Vec<DartParam>,
352 pub body: Vec<DartStmt>,
353 pub is_async: bool,
354 pub is_static: bool,
355 pub is_abstract: bool,
356 pub type_params: Vec<String>,
357 pub doc: Option<String>,
358}
359impl DartFunction {
360 pub fn new(name: impl Into<String>, return_type: DartType) -> Self {
361 DartFunction {
362 name: name.into(),
363 return_type,
364 params: Vec::new(),
365 body: Vec::new(),
366 is_async: false,
367 is_static: false,
368 is_abstract: false,
369 type_params: Vec::new(),
370 doc: None,
371 }
372 }
373}
374#[allow(dead_code)]
376#[derive(Debug, Clone)]
377pub enum DartAnnotation {
378 Override,
380 Deprecated,
382 VisibleForTesting,
384 Immutable,
386 Sealed,
388 Custom(String, Vec<String>),
390}
391#[derive(Debug, Clone, PartialEq)]
393pub struct DartParam {
394 pub ty: DartType,
395 pub name: String,
396 pub is_named: bool,
397 pub is_required: bool,
398 pub default_value: Option<DartExpr>,
399}
400impl DartParam {
401 pub fn positional(ty: DartType, name: impl Into<String>) -> Self {
402 DartParam {
403 ty,
404 name: name.into(),
405 is_named: false,
406 is_required: true,
407 default_value: None,
408 }
409 }
410 pub fn named_required(ty: DartType, name: impl Into<String>) -> Self {
411 DartParam {
412 ty,
413 name: name.into(),
414 is_named: true,
415 is_required: true,
416 default_value: None,
417 }
418 }
419 pub fn named_optional(ty: DartType, name: impl Into<String>, default: DartExpr) -> Self {
420 DartParam {
421 ty,
422 name: name.into(),
423 is_named: true,
424 is_required: false,
425 default_value: Some(default),
426 }
427 }
428}
429pub struct DartBackend {
431 pub(super) var_counter: u64,
433 pub(super) keywords: HashSet<&'static str>,
435 pub(super) name_cache: HashMap<String, String>,
437 pub(super) indent_width: usize,
439}
440impl DartBackend {
441 pub fn new() -> Self {
442 let mut keywords = HashSet::new();
443 for kw in DART_KEYWORDS {
444 keywords.insert(*kw);
445 }
446 DartBackend {
447 var_counter: 0,
448 keywords,
449 name_cache: HashMap::new(),
450 indent_width: 2,
451 }
452 }
453 pub fn fresh_var(&mut self) -> String {
455 let id = self.var_counter;
456 self.var_counter += 1;
457 format!("_v{}", id)
458 }
459 pub fn mangle_name(&mut self, name: &str) -> String {
461 if let Some(cached) = self.name_cache.get(name) {
462 return cached.clone();
463 }
464 let mangled = mangle_dart_ident(name, &self.keywords);
465 self.name_cache.insert(name.to_string(), mangled.clone());
466 mangled
467 }
468 pub fn emit_module(&mut self, module: &DartModule) -> String {
470 let mut out = String::new();
471 if let Some(part) = &module.part_of {
472 out.push_str(&format!("part of '{}';\n\n", part));
473 }
474 for import in &module.imports {
475 out.push_str(&self.emit_import(import));
476 }
477 if !module.imports.is_empty() {
478 out.push('\n');
479 }
480 for exp in &module.exports {
481 out.push_str(&format!("export '{}';\n", exp));
482 }
483 if !module.exports.is_empty() {
484 out.push('\n');
485 }
486 for (ty, name, init) in &module.globals {
487 out.push_str(&format!("final {} {} = {};\n", ty, name, init));
488 }
489 if !module.globals.is_empty() {
490 out.push('\n');
491 }
492 for func in &module.functions {
493 out.push_str(&self.emit_function(func, 0));
494 out.push('\n');
495 }
496 for class in &module.classes {
497 out.push_str(&self.emit_class(class, 0));
498 out.push('\n');
499 }
500 out
501 }
502 pub fn emit_import(&self, import: &DartImport) -> String {
504 let mut line = format!("import '{}'", import.uri);
505 if import.is_deferred {
506 line.push_str(" deferred");
507 }
508 if let Some(prefix) = &import.as_prefix {
509 line.push_str(&format!(" as {}", prefix));
510 }
511 if !import.show.is_empty() {
512 line.push_str(&format!(" show {}", import.show.join(", ")));
513 }
514 if !import.hide.is_empty() {
515 line.push_str(&format!(" hide {}", import.hide.join(", ")));
516 }
517 line.push_str(";\n");
518 line
519 }
520 pub fn emit_class(&self, class: &DartClass, depth: usize) -> String {
522 let indent = self.indent(depth);
523 let mut out = String::new();
524 if let Some(doc) = &class.doc {
525 for line in doc.lines() {
526 out.push_str(&format!("{}/// {}\n", indent, line));
527 }
528 }
529 if class.is_abstract {
530 out.push_str(&format!("{}abstract ", indent));
531 } else {
532 out.push_str(&indent);
533 }
534 out.push_str("class ");
535 out.push_str(&class.name);
536 if !class.type_params.is_empty() {
537 out.push('<');
538 out.push_str(&class.type_params.join(", "));
539 out.push('>');
540 }
541 if let Some(ext) = &class.extends {
542 out.push_str(&format!(" extends {}", ext));
543 }
544 if !class.mixins.is_empty() {
545 out.push_str(&format!(" with {}", class.mixins.join(", ")));
546 }
547 if !class.implements.is_empty() {
548 out.push_str(&format!(" implements {}", class.implements.join(", ")));
549 }
550 out.push_str(" {\n");
551 let inner = self.indent(depth + 1);
552 for field in &class.fields {
553 if let Some(doc) = &field.doc {
554 out.push_str(&format!("{}/// {}\n", inner, doc));
555 }
556 let mut modifiers = String::new();
557 if field.is_static {
558 modifiers.push_str("static ");
559 }
560 if field.is_final {
561 modifiers.push_str("final ");
562 }
563 if field.is_late {
564 modifiers.push_str("late ");
565 }
566 if let Some(init) = &field.default_value {
567 out.push_str(&format!(
568 "{}{}{} {} = {};\n",
569 inner, modifiers, field.ty, field.name, init
570 ));
571 } else {
572 out.push_str(&format!(
573 "{}{}{} {};\n",
574 inner, modifiers, field.ty, field.name
575 ));
576 }
577 }
578 if !class.fields.is_empty() && (!class.constructors.is_empty() || !class.methods.is_empty())
579 {
580 out.push('\n');
581 }
582 for ctor in &class.constructors {
583 out.push_str(&self.emit_constructor(ctor, &class.name, depth + 1));
584 out.push('\n');
585 }
586 for method in &class.methods {
587 out.push_str(&self.emit_function(method, depth + 1));
588 out.push('\n');
589 }
590 out.push_str(&format!("{}}}\n", indent));
591 out
592 }
593 pub fn emit_constructor(&self, ctor: &DartFunction, class_name: &str, depth: usize) -> String {
595 let indent = self.indent(depth);
596 let mut out = String::new();
597 if let Some(doc) = &ctor.doc {
598 out.push_str(&format!("{}/// {}\n", indent, doc));
599 }
600 if ctor.is_static {
601 out.push_str(&format!("{}static ", indent));
602 } else {
603 out.push_str(&indent);
604 }
605 let ctor_name = if ctor.name.is_empty() {
606 class_name.to_string()
607 } else {
608 format!("{}.{}", class_name, ctor.name)
609 };
610 out.push_str(&ctor_name);
611 out.push('(');
612 out.push_str(&self.emit_params(&ctor.params));
613 out.push(')');
614 if ctor.is_abstract {
615 out.push_str(";\n");
616 } else {
617 out.push_str(" {\n");
618 for stmt in &ctor.body {
619 out.push_str(&self.emit_stmt(stmt, depth + 1));
620 }
621 out.push_str(&format!("{}}}\n", indent));
622 }
623 out
624 }
625 pub fn emit_function(&self, func: &DartFunction, depth: usize) -> String {
627 let indent = self.indent(depth);
628 let mut out = String::new();
629 if let Some(doc) = &func.doc {
630 for line in doc.lines() {
631 out.push_str(&format!("{}/// {}\n", indent, line));
632 }
633 }
634 if func.is_static {
635 out.push_str(&format!("{}static ", indent));
636 } else {
637 out.push_str(&indent);
638 }
639 let async_suffix = if func.is_async { " async" } else { "" };
640 let ret_ty = if func.is_async {
641 match &func.return_type {
642 DartType::DtFuture(_) => format!("{}", func.return_type),
643 other => format!("Future<{}>", other),
644 }
645 } else {
646 format!("{}", func.return_type)
647 };
648 let type_params_str = if func.type_params.is_empty() {
649 String::new()
650 } else {
651 format!("<{}>", func.type_params.join(", "))
652 };
653 out.push_str(&format!(
654 "{} {}{}({}){}",
655 ret_ty,
656 func.name,
657 type_params_str,
658 self.emit_params(&func.params),
659 async_suffix,
660 ));
661 if func.is_abstract {
662 out.push_str(";\n");
663 } else {
664 out.push_str(" {\n");
665 for stmt in &func.body {
666 out.push_str(&self.emit_stmt(stmt, depth + 1));
667 }
668 out.push_str(&format!("{}}}\n", indent));
669 }
670 out
671 }
672 pub fn emit_getter(&self, func: &DartFunction, depth: usize) -> String {
674 let indent = self.indent(depth);
675 let mut out = String::new();
676 if let Some(doc) = &func.doc {
677 for line in doc.lines() {
678 out.push_str(&format!("{}/// {}\n", indent, line));
679 }
680 }
681 if func.is_static {
682 out.push_str(&format!("{}static ", indent));
683 } else {
684 out.push_str(&indent);
685 }
686 out.push_str(&format!("{} get {}", func.return_type, func.name));
687 if func.is_abstract {
688 out.push_str(";\n");
689 } else {
690 out.push_str(" {\n");
691 for stmt in &func.body {
692 out.push_str(&self.emit_stmt(stmt, depth + 1));
693 }
694 out.push_str(&format!("{}}}\n", indent));
695 }
696 out
697 }
698 pub fn emit_params(&self, params: &[DartParam]) -> String {
700 let positional: Vec<&DartParam> = params.iter().filter(|p| !p.is_named).collect();
701 let named: Vec<&DartParam> = params.iter().filter(|p| p.is_named).collect();
702 let mut parts: Vec<String> = positional
703 .iter()
704 .map(|p| format!("{} {}", p.ty, p.name))
705 .collect();
706 if !named.is_empty() {
707 let named_parts: Vec<String> = named
708 .iter()
709 .map(|p| {
710 let req = if p.is_required { "required " } else { "" };
711 if let Some(def) = &p.default_value {
712 format!("{}{} {} = {}", req, p.ty, p.name, def)
713 } else {
714 format!("{}{} {}", req, p.ty, p.name)
715 }
716 })
717 .collect();
718 parts.push(format!("{{{}}}", named_parts.join(", ")));
719 }
720 parts.join(", ")
721 }
722 pub fn emit_stmt(&self, stmt: &DartStmt, depth: usize) -> String {
724 let indent = self.indent(depth);
725 match stmt {
726 DartStmt::VarDecl(ty, name, init) => {
727 format!("{}{} {} = {};\n", indent, ty, name, init)
728 }
729 DartStmt::VarInferred(name, init) => {
730 format!("{}var {} = {};\n", indent, name, init)
731 }
732 DartStmt::FinalDecl(ty, name, init) => {
733 format!("{}final {} {} = {};\n", indent, ty, name, init)
734 }
735 DartStmt::ConstDecl(ty, name, init) => {
736 format!("{}const {} {} = {};\n", indent, ty, name, init)
737 }
738 DartStmt::Assign(name, val) => format!("{}{} = {};\n", indent, name, val),
739 DartStmt::FieldAssign(obj, field, val) => {
740 format!("{}{}.{} = {};\n", indent, obj, field, val)
741 }
742 DartStmt::IndexAssign(obj, idx, val) => {
743 format!("{}{}[{}] = {};\n", indent, obj, idx, val)
744 }
745 DartStmt::Return(None) => format!("{}return;\n", indent),
746 DartStmt::Return(Some(expr)) => format!("{}return {};\n", indent, expr),
747 DartStmt::Expr(expr) => format!("{}{};\n", indent, expr),
748 DartStmt::If(cond, then, else_) => {
749 let mut out = format!("{}if ({}) {{\n", indent, cond);
750 for s in then {
751 out.push_str(&self.emit_stmt(s, depth + 1));
752 }
753 if !else_.is_empty() {
754 out.push_str(&format!("{}}} else {{\n", indent));
755 for s in else_ {
756 out.push_str(&self.emit_stmt(s, depth + 1));
757 }
758 }
759 out.push_str(&format!("{}}}\n", indent));
760 out
761 }
762 DartStmt::While(cond, body) => {
763 let mut out = format!("{}while ({}) {{\n", indent, cond);
764 for s in body {
765 out.push_str(&self.emit_stmt(s, depth + 1));
766 }
767 out.push_str(&format!("{}}}\n", indent));
768 out
769 }
770 DartStmt::DoWhile(body, cond) => {
771 let mut out = format!("{}do {{\n", indent);
772 for s in body {
773 out.push_str(&self.emit_stmt(s, depth + 1));
774 }
775 out.push_str(&format!("{}}} while ({});\n", indent, cond));
776 out
777 }
778 DartStmt::For(init, cond, update, body) => {
779 let init_str = self.emit_stmt(init, 0).trim_end_matches('\n').to_string();
780 let update_str = self
781 .emit_stmt(update, 0)
782 .trim_end_matches(";\n")
783 .trim()
784 .to_string();
785 let mut out = format!(
786 "{}for ({}; {}; {}) {{\n",
787 indent,
788 init_str.trim_start(),
789 cond,
790 update_str
791 );
792 for s in body {
793 out.push_str(&self.emit_stmt(s, depth + 1));
794 }
795 out.push_str(&format!("{}}}\n", indent));
796 out
797 }
798 DartStmt::ForIn(var, iter, body) => {
799 let mut out = format!("{}for (final {} in {}) {{\n", indent, var, iter);
800 for s in body {
801 out.push_str(&self.emit_stmt(s, depth + 1));
802 }
803 out.push_str(&format!("{}}}\n", indent));
804 out
805 }
806 DartStmt::Break => format!("{}break;\n", indent),
807 DartStmt::Continue => format!("{}continue;\n", indent),
808 DartStmt::Throw(expr) => format!("{}throw {};\n", indent, expr),
809 DartStmt::TryCatch(body, catch_var, handler, fin) => {
810 let mut out = format!("{}try {{\n", indent);
811 for s in body {
812 out.push_str(&self.emit_stmt(s, depth + 1));
813 }
814 out.push_str(&format!("{}}} catch ({}) {{\n", indent, catch_var));
815 for s in handler {
816 out.push_str(&self.emit_stmt(s, depth + 1));
817 }
818 if !fin.is_empty() {
819 out.push_str(&format!("{}}} finally {{\n", indent));
820 for s in fin {
821 out.push_str(&self.emit_stmt(s, depth + 1));
822 }
823 }
824 out.push_str(&format!("{}}}\n", indent));
825 out
826 }
827 DartStmt::Switch(expr, cases, default) => {
828 let mut out = format!("{}switch ({}) {{\n", indent, expr);
829 for (val, stmts) in cases {
830 out.push_str(&format!("{} case {}:\n", indent, val));
831 for s in stmts {
832 out.push_str(&self.emit_stmt(s, depth + 2));
833 }
834 out.push_str(&format!("{} break;\n", indent));
835 }
836 if !default.is_empty() {
837 out.push_str(&format!("{} default:\n", indent));
838 for s in default {
839 out.push_str(&self.emit_stmt(s, depth + 2));
840 }
841 }
842 out.push_str(&format!("{}}}\n", indent));
843 out
844 }
845 DartStmt::Assert(expr) => format!("{}assert({});\n", indent, expr),
846 DartStmt::Block(stmts) => {
847 let mut out = format!("{}{{\n", indent);
848 for s in stmts {
849 out.push_str(&self.emit_stmt(s, depth + 1));
850 }
851 out.push_str(&format!("{}}}\n", indent));
852 out
853 }
854 DartStmt::Raw(code) => format!("{}{}\n", indent, code),
855 }
856 }
857 pub(super) fn indent(&self, depth: usize) -> String {
858 " ".repeat(depth * self.indent_width)
859 }
860 pub fn compile_lcnf_function(&mut self, func: &LcnfFunDecl) -> Result<DartFunction, String> {
862 let name = self.mangle_name(&func.name.to_string());
863 let ret_ty = lcnf_type_to_dart(&func.ret_type);
864 let mut params = Vec::new();
865 for param in &func.params {
866 let pname = format!("_x{}", param.id.0);
867 let pty = lcnf_type_to_dart(¶m.ty);
868 params.push(DartParam::positional(pty, pname));
869 }
870 let mut body_stmts = Vec::new();
871 let result_expr = self.compile_expr(&func.body, &mut body_stmts)?;
872 body_stmts.push(DartStmt::Return(Some(result_expr)));
873 let mut dart_fn = DartFunction::new(name, ret_ty);
874 dart_fn.params = params;
875 dart_fn.body = body_stmts;
876 Ok(dart_fn)
877 }
878 pub fn compile_lcnf_module(&mut self, module: &LcnfModule) -> Result<DartModule, String> {
880 let mut dart_module = DartModule::new();
881 dart_module.imports.push(DartImport::simple("dart:core"));
882 dart_module
883 .imports
884 .push(DartImport::simple("dart:collection"));
885 let ctor_names = collect_ctor_names(module);
886 for ctor_name in &ctor_names {
887 dart_module.classes.push(make_ctor_class(ctor_name));
888 }
889 dart_module
890 .functions
891 .push(DartFunction::new("_unreachable", DartType::DtDynamic));
892 for func in &module.fun_decls {
893 let dart_fn = self.compile_lcnf_function(func)?;
894 dart_module.functions.push(dart_fn);
895 }
896 Ok(dart_module)
897 }
898 pub(super) fn compile_expr(
899 &mut self,
900 expr: &LcnfExpr,
901 stmts: &mut Vec<DartStmt>,
902 ) -> Result<DartExpr, String> {
903 match expr {
904 LcnfExpr::Return(arg) => Ok(self.compile_arg(arg)),
905 LcnfExpr::Unreachable => {
906 stmts.push(DartStmt::Throw(DartExpr::New(
907 "StateError".to_string(),
908 None,
909 vec![DartExpr::Lit(DartLit::Str(
910 "OxiLean: unreachable".to_string(),
911 ))],
912 )));
913 Ok(DartExpr::Lit(DartLit::Null))
914 }
915 LcnfExpr::TailCall(func, args) => {
916 let callee = self.compile_arg(func);
917 let dart_args: Vec<DartExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
918 Ok(DartExpr::Call(Box::new(callee), dart_args))
919 }
920 LcnfExpr::Let {
921 id,
922 name: _,
923 ty,
924 value,
925 body,
926 } => {
927 let var_name = format!("_x{}", id.0);
928 let dart_ty = lcnf_type_to_dart(ty);
929 let val_expr = self.compile_let_value(value)?;
930 stmts.push(DartStmt::VarDecl(dart_ty, var_name, val_expr));
931 self.compile_expr(body, stmts)
932 }
933 LcnfExpr::Case {
934 scrutinee,
935 scrutinee_ty: _,
936 alts,
937 default,
938 } => {
939 let scrutinee_expr = DartExpr::Var(format!("_x{}", scrutinee.0));
940 let result_var = self.fresh_var();
941 let mut cases: Vec<(DartExpr, Vec<DartStmt>)> = Vec::new();
942 for alt in alts {
943 let mut branch_stmts: Vec<DartStmt> = Vec::new();
944 for (idx, param) in alt.params.iter().enumerate() {
945 let field_access = DartExpr::Index(
946 Box::new(DartExpr::Field(
947 Box::new(scrutinee_expr.clone()),
948 "fields".to_string(),
949 )),
950 Box::new(DartExpr::Lit(DartLit::Int(idx as i64))),
951 );
952 let pname = format!("_x{}", param.id.0);
953 let pty = lcnf_type_to_dart(¶m.ty);
954 branch_stmts.push(DartStmt::FinalDecl(pty, pname, field_access));
955 }
956 let branch_result = self.compile_expr(&alt.body, &mut branch_stmts)?;
957 branch_stmts.push(DartStmt::Assign(result_var.clone(), branch_result));
958 let tag_val = DartExpr::Lit(DartLit::Int(alt.ctor_tag as i64));
959 cases.push((tag_val, branch_stmts));
960 }
961 let mut default_stmts: Vec<DartStmt> = Vec::new();
962 if let Some(def) = default {
963 let def_result = self.compile_expr(def, &mut default_stmts)?;
964 default_stmts.push(DartStmt::Assign(result_var.clone(), def_result));
965 } else {
966 default_stmts.push(DartStmt::Throw(DartExpr::New(
967 "StateError".to_string(),
968 None,
969 vec![DartExpr::Lit(DartLit::Str(
970 "OxiLean: unreachable branch".to_string(),
971 ))],
972 )));
973 }
974 let discriminant = DartExpr::Field(Box::new(scrutinee_expr), "tag".to_string());
975 stmts.push(DartStmt::VarDecl(
976 DartType::DtDynamic,
977 result_var.clone(),
978 DartExpr::Lit(DartLit::Null),
979 ));
980 stmts.push(DartStmt::Switch(discriminant, cases, default_stmts));
981 Ok(DartExpr::Var(result_var))
982 }
983 }
984 }
985 pub(super) fn compile_let_value(&mut self, value: &LcnfLetValue) -> Result<DartExpr, String> {
986 match value {
987 LcnfLetValue::Lit(lit) => Ok(self.compile_lit(lit)),
988 LcnfLetValue::Erased => Ok(DartExpr::Lit(DartLit::Null)),
989 LcnfLetValue::FVar(id) => Ok(DartExpr::Var(format!("_x{}", id.0))),
990 LcnfLetValue::App(func, args) => {
991 let callee = self.compile_arg(func);
992 let dart_args: Vec<DartExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
993 Ok(DartExpr::Call(Box::new(callee), dart_args))
994 }
995 LcnfLetValue::Proj(_name, idx, var) => {
996 let base = DartExpr::Var(format!("_x{}", var.0));
997 Ok(DartExpr::Index(
998 Box::new(DartExpr::Field(Box::new(base), "fields".to_string())),
999 Box::new(DartExpr::Lit(DartLit::Int(*idx as i64))),
1000 ))
1001 }
1002 LcnfLetValue::Ctor(name, _tag, args) => {
1003 let ctor_name = self.mangle_name(name);
1004 let dart_args: Vec<DartExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1005 Ok(DartExpr::New(ctor_name, None, dart_args))
1006 }
1007 LcnfLetValue::Reset(_var) => Ok(DartExpr::Lit(DartLit::Null)),
1008 LcnfLetValue::Reuse(_slot, name, _tag, args) => {
1009 let ctor_name = self.mangle_name(name);
1010 let dart_args: Vec<DartExpr> = args.iter().map(|a| self.compile_arg(a)).collect();
1011 Ok(DartExpr::New(ctor_name, None, dart_args))
1012 }
1013 }
1014 }
1015 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> DartExpr {
1016 match arg {
1017 LcnfArg::Var(id) => DartExpr::Var(format!("_x{}", id.0)),
1018 LcnfArg::Lit(lit) => self.compile_lit(lit),
1019 LcnfArg::Erased => DartExpr::Lit(DartLit::Null),
1020 LcnfArg::Type(_) => DartExpr::Lit(DartLit::Null),
1021 }
1022 }
1023 pub(super) fn compile_lit(&self, lit: &LcnfLit) -> DartExpr {
1024 match lit {
1025 LcnfLit::Nat(n) => DartExpr::Lit(DartLit::Int(*n as i64)),
1026 LcnfLit::Str(s) => DartExpr::Lit(DartLit::Str(s.clone())),
1027 }
1028 }
1029}
1030#[allow(dead_code)]
1032#[derive(Debug, Clone)]
1033pub struct DartFile {
1034 pub imports: Vec<DartImport>,
1035 pub type_aliases: Vec<DartTypeAlias>,
1036 pub enums: Vec<DartEnum>,
1037 pub classes: Vec<DartClass>,
1038 pub mixins: Vec<DartMixin>,
1039 pub top_level_functions: Vec<DartFunction>,
1040 pub top_level_vars: Vec<(DartType, String, Option<DartExpr>)>,
1041 pub library_name: Option<String>,
1042}
1043#[allow(dead_code)]
1044impl DartFile {
1045 pub fn new() -> Self {
1046 DartFile {
1047 imports: vec![],
1048 type_aliases: vec![],
1049 enums: vec![],
1050 classes: vec![],
1051 mixins: vec![],
1052 top_level_functions: vec![],
1053 top_level_vars: vec![],
1054 library_name: None,
1055 }
1056 }
1057 pub fn with_library(mut self, name: impl Into<String>) -> Self {
1058 self.library_name = Some(name.into());
1059 self
1060 }
1061 pub fn add_import(&mut self, imp: DartImport) {
1062 self.imports.push(imp);
1063 }
1064 pub fn add_enum(&mut self, e: DartEnum) {
1065 self.enums.push(e);
1066 }
1067 pub fn add_class(&mut self, c: DartClass) {
1068 self.classes.push(c);
1069 }
1070 pub fn add_function(&mut self, f: DartFunction) {
1071 self.top_level_functions.push(f);
1072 }
1073 pub fn add_type_alias(&mut self, ta: DartTypeAlias) {
1074 self.type_aliases.push(ta);
1075 }
1076 pub fn add_mixin(&mut self, m: DartMixin) {
1077 self.mixins.push(m);
1078 }
1079 pub fn emit(&self, backend: &DartBackend) -> String {
1081 let mut out = String::new();
1082 if let Some(lib) = &self.library_name {
1083 out.push_str(&format!("library {};\n\n", lib));
1084 }
1085 for imp in &self.imports {
1086 out.push_str(&imp.emit());
1087 }
1088 if !self.imports.is_empty() {
1089 out.push('\n');
1090 }
1091 for ta in &self.type_aliases {
1092 out.push_str(&ta.emit());
1093 }
1094 if !self.type_aliases.is_empty() {
1095 out.push('\n');
1096 }
1097 for e in &self.enums {
1098 out.push_str(&e.emit());
1099 out.push('\n');
1100 }
1101 for m in &self.mixins {
1102 out.push_str(&m.emit(backend, 0));
1103 out.push('\n');
1104 }
1105 for c in &self.classes {
1106 out.push_str(&backend.emit_class(c, 0));
1107 out.push('\n');
1108 }
1109 for (ty, name, init) in &self.top_level_vars {
1110 if let Some(val) = init {
1111 out.push_str(&format!("{} {} = {};\n", ty, name, val));
1112 } else {
1113 out.push_str(&format!("late {} {};\n", ty, name));
1114 }
1115 }
1116 if !self.top_level_vars.is_empty() {
1117 out.push('\n');
1118 }
1119 for func in &self.top_level_functions {
1120 out.push_str(&backend.emit_function(func, 0));
1121 }
1122 out
1123 }
1124}
1125#[allow(dead_code)]
1127#[derive(Debug, Clone)]
1128pub struct DartStreamBuilder {
1129 pub item_type: DartType,
1130}
1131#[allow(dead_code)]
1132impl DartStreamBuilder {
1133 pub fn new(item_type: DartType) -> Self {
1134 DartStreamBuilder { item_type }
1135 }
1136 pub fn from_iterable(items: Vec<DartExpr>) -> DartExpr {
1138 DartExpr::MethodCall(
1139 Box::new(DartExpr::Var("Stream".to_string())),
1140 "fromIterable".to_string(),
1141 vec![DartExpr::ListLit(items)],
1142 )
1143 }
1144 pub fn listen(stream: DartExpr, param: &str, body: Vec<DartStmt>) -> DartStmt {
1146 let backend = DartBackend::new();
1147 let body_str: String = body.iter().map(|s| backend.emit_stmt(s, 1)).collect();
1148 let closure = DartExpr::Raw(format!("({}) {{\n{}}}", param, body_str));
1149 DartStmt::Expr(DartExpr::MethodCall(
1150 Box::new(stream),
1151 "listen".to_string(),
1152 vec![closure],
1153 ))
1154 }
1155 pub fn controller_decl(&self, name: &str) -> DartStmt {
1157 let ty = DartType::DtGeneric("StreamController".to_string(), vec![self.item_type.clone()]);
1158 DartStmt::VarDecl(
1159 ty.clone(),
1160 name.to_string(),
1161 DartExpr::New("StreamController".to_string(), None, vec![]),
1162 )
1163 }
1164}
1165#[allow(dead_code)]
1167#[derive(Debug, Clone)]
1168pub struct DartSealedHierarchy {
1169 pub base_name: String,
1170 pub variants: Vec<DartClass>,
1171}
1172#[allow(dead_code)]
1173impl DartSealedHierarchy {
1174 pub fn new(base_name: impl Into<String>) -> Self {
1175 DartSealedHierarchy {
1176 base_name: base_name.into(),
1177 variants: vec![],
1178 }
1179 }
1180 pub fn add_variant(&mut self, variant: DartClass) {
1181 self.variants.push(variant);
1182 }
1183 pub fn emit(&self, backend: &DartBackend) -> String {
1185 let mut out = String::new();
1186 out.push_str(&format!(
1187 "sealed class {} {{\n const {}();\n}}\n\n",
1188 self.base_name, self.base_name
1189 ));
1190 for variant in &self.variants {
1191 out.push_str(&backend.emit_class(variant, 0));
1192 out.push('\n');
1193 }
1194 out
1195 }
1196}
1197#[allow(dead_code)]
1199#[derive(Debug, Clone)]
1200pub struct DartTypeAlias {
1201 pub name: String,
1202 pub ty: DartType,
1203 pub doc: Option<String>,
1204}
1205#[allow(dead_code)]
1206impl DartTypeAlias {
1207 pub fn new(name: impl Into<String>, ty: DartType) -> Self {
1208 DartTypeAlias {
1209 name: name.into(),
1210 ty,
1211 doc: None,
1212 }
1213 }
1214 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
1215 self.doc = Some(doc.into());
1216 self
1217 }
1218 pub fn emit(&self) -> String {
1219 let mut out = String::new();
1220 if let Some(doc) = &self.doc {
1221 out.push_str(&format!("/// {}\n", doc));
1222 }
1223 out.push_str(&format!("typedef {} = {};\n", self.name, self.ty));
1224 out
1225 }
1226}
1227#[derive(Debug, Clone, PartialEq)]
1229pub enum DartStmt {
1230 VarDecl(DartType, String, DartExpr),
1232 VarInferred(String, DartExpr),
1234 FinalDecl(DartType, String, DartExpr),
1236 ConstDecl(DartType, String, DartExpr),
1238 Assign(String, DartExpr),
1240 FieldAssign(DartExpr, String, DartExpr),
1242 IndexAssign(DartExpr, DartExpr, DartExpr),
1244 Return(Option<DartExpr>),
1246 Expr(DartExpr),
1248 If(DartExpr, Vec<DartStmt>, Vec<DartStmt>),
1250 While(DartExpr, Vec<DartStmt>),
1252 DoWhile(Vec<DartStmt>, DartExpr),
1254 For(Box<DartStmt>, DartExpr, Box<DartStmt>, Vec<DartStmt>),
1256 ForIn(String, DartExpr, Vec<DartStmt>),
1258 Break,
1260 Continue,
1262 Throw(DartExpr),
1264 TryCatch(Vec<DartStmt>, String, Vec<DartStmt>, Vec<DartStmt>),
1266 Switch(DartExpr, Vec<(DartExpr, Vec<DartStmt>)>, Vec<DartStmt>),
1268 Assert(DartExpr),
1270 Block(Vec<DartStmt>),
1272 Raw(String),
1274}
1275#[allow(dead_code)]
1277#[derive(Debug, Clone)]
1278pub struct DartImportExt {
1279 pub uri: String,
1280 pub prefix: Option<String>,
1281 pub show: Vec<String>,
1282 pub hide: Vec<String>,
1283 pub is_deferred: bool,
1284}
1285#[allow(dead_code)]
1286impl DartImportExt {
1287 pub fn simple(uri: impl Into<String>) -> Self {
1288 DartImportExt {
1289 uri: uri.into(),
1290 prefix: None,
1291 show: vec![],
1292 hide: vec![],
1293 is_deferred: false,
1294 }
1295 }
1296 pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
1297 self.prefix = Some(prefix.into());
1298 self
1299 }
1300 pub fn show_identifiers(mut self, ids: Vec<String>) -> Self {
1301 self.show = ids;
1302 self
1303 }
1304 pub fn hide_identifiers(mut self, ids: Vec<String>) -> Self {
1305 self.hide = ids;
1306 self
1307 }
1308 pub fn deferred(mut self) -> Self {
1309 self.is_deferred = true;
1310 self
1311 }
1312 pub fn emit(&self) -> String {
1313 let mut out = format!("import '{}'", self.uri);
1314 if self.is_deferred {
1315 out.push_str(" deferred");
1316 }
1317 if let Some(prefix) = &self.prefix {
1318 out.push_str(&format!(" as {}", prefix));
1319 }
1320 if !self.show.is_empty() {
1321 out.push_str(&format!(" show {}", self.show.join(", ")));
1322 }
1323 if !self.hide.is_empty() {
1324 out.push_str(&format!(" hide {}", self.hide.join(", ")));
1325 }
1326 out.push_str(";\n");
1327 out
1328 }
1329}
1330#[allow(dead_code)]
1332#[derive(Debug, Clone, Default)]
1333pub struct DartCodeMetrics {
1334 pub total_lines: usize,
1335 pub class_count: usize,
1336 pub function_count: usize,
1337 pub import_count: usize,
1338}
1339#[allow(dead_code)]
1340impl DartCodeMetrics {
1341 pub fn collect(file: &DartFile) -> Self {
1342 DartCodeMetrics {
1343 total_lines: 0,
1344 class_count: file.classes.len(),
1345 function_count: file.top_level_functions.len(),
1346 import_count: file.imports.len(),
1347 }
1348 }
1349 pub fn update_lines(&mut self, source: &str) {
1350 self.total_lines = source.lines().count();
1351 }
1352}
1353#[derive(Debug, Clone)]
1355pub struct DartField {
1356 pub ty: DartType,
1357 pub name: String,
1358 pub is_final: bool,
1359 pub is_static: bool,
1360 pub is_late: bool,
1361 pub default_value: Option<DartExpr>,
1362 pub doc: Option<String>,
1363}
1364impl DartField {
1365 pub fn new(ty: DartType, name: impl Into<String>) -> Self {
1366 DartField {
1367 ty,
1368 name: name.into(),
1369 is_final: false,
1370 is_static: false,
1371 is_late: false,
1372 default_value: None,
1373 doc: None,
1374 }
1375 }
1376 pub fn final_field(ty: DartType, name: impl Into<String>) -> Self {
1377 DartField {
1378 ty,
1379 name: name.into(),
1380 is_final: true,
1381 is_static: false,
1382 is_late: false,
1383 default_value: None,
1384 doc: None,
1385 }
1386 }
1387}
1388#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1390pub enum DartType {
1391 DtInt,
1393 DtDouble,
1395 DtBool,
1397 DtString,
1399 DtVoid,
1401 DtDynamic,
1403 DtObject,
1405 DtNull,
1407 DtNullable(Box<DartType>),
1409 DtList(Box<DartType>),
1411 DtMap(Box<DartType>, Box<DartType>),
1413 DtSet(Box<DartType>),
1415 DtFuture(Box<DartType>),
1417 DtStream(Box<DartType>),
1419 DtFunction(Vec<DartType>, Box<DartType>),
1421 DtNamed(String),
1423 DtGeneric(String, Vec<DartType>),
1425}
1426#[allow(dead_code)]
1428pub struct DartNullSafety;
1429#[allow(dead_code)]
1430impl DartNullSafety {
1431 pub fn assert_non_null(expr: DartExpr) -> DartExpr {
1433 DartExpr::Raw(format!("{}!", expr))
1434 }
1435 pub fn coalesce(expr: DartExpr, fallback: DartExpr) -> DartExpr {
1437 DartExpr::NullCoalesce(Box::new(expr), Box::new(fallback))
1438 }
1439 pub fn safe_field(expr: DartExpr, field: impl Into<String>) -> DartExpr {
1441 DartExpr::NullAware(Box::new(expr), field.into())
1442 }
1443 pub fn guard_not_null(var: &str, _ty: DartType) -> DartStmt {
1445 DartStmt::If(
1446 DartExpr::BinOp(
1447 Box::new(DartExpr::Var(var.to_string())),
1448 "==".to_string(),
1449 Box::new(DartExpr::Lit(DartLit::Null)),
1450 ),
1451 vec![DartStmt::Throw(DartExpr::New(
1452 "ArgumentError".to_string(),
1453 None,
1454 vec![DartExpr::Lit(DartLit::Str(format!(
1455 "{} must not be null",
1456 var
1457 )))],
1458 ))],
1459 vec![],
1460 )
1461 }
1462 pub fn nullable_decl(ty: DartType, name: &str) -> DartStmt {
1464 DartStmt::VarDecl(
1465 DartType::DtNullable(Box::new(ty)),
1466 name.to_string(),
1467 DartExpr::Lit(DartLit::Null),
1468 )
1469 }
1470}