espforge_lib/resolver/
ruchy_bridge.rs1use anyhow::{Result, anyhow};
2use ruchy::backend::Transpiler;
3use ruchy::frontend::Parser;
4use ruchy::frontend::ast::{ExprKind, TypeKind};
5
6pub struct RuchyOutput {
7 pub setup: String,
8 pub loop_body: String,
9 pub variables: Vec<String>,
10 pub task_definitions: Vec<String>,
11 pub task_names: Vec<String>,
12 pub task_spawns: Vec<String>, }
14
15pub fn compile_ruchy_script(raw_source: &str, enable_async: bool) -> Result<RuchyOutput> {
16 let source = raw_source.replace("\r\n", "\n");
18
19 let mut parser = Parser::new(&source);
20 let ast = parser
21 .parse()
22 .map_err(|e| anyhow!("Failed to parse Ruchy code: {:?}", e))?;
23 let mut transpiler = Transpiler::new();
24
25 let mut setup_body = String::new();
26 let mut loop_body = String::new();
27 let mut variables = Vec::new();
28 let mut task_definitions = Vec::new();
29 let mut task_names = Vec::new();
30 let mut task_spawns = Vec::new();
31
32 if let ExprKind::Block(exprs) = ast.kind {
33 for expr in exprs {
34 let is_disabled = expr.attributes.iter().any(|attr| attr.name == "disabled");
35 if is_disabled {
36 continue;
37 }
38 let has_task_attr = expr.attributes.iter().any(|attr| attr.name == "task");
39
40 match expr.kind {
41 ExprKind::Function { name, body, params, .. } => {
42 let is_task = has_task_attr || name.starts_with("task_");
43
44 if let ExprKind::Block(ref stmts) = body.kind {
45 transpiler.analyze_mutability(stmts);
46 } else {
47 transpiler.analyze_mutability(&[body.as_ref().clone()]);
48 }
49 let token_stream = transpiler.transpile_expr(&body)?;
50 let raw_code = token_stream.to_string();
51
52 let indent = if is_task || name == "forever" { 2 } else { 1 };
53 let formatted_body = format_rust_code(&raw_code, indent, enable_async);
54
55 if is_task {
56 if !enable_async {
57 return Err(anyhow!(
58 "Function '{}' identified as task but 'enable_async' is false in configuration.",
59 name
60 ));
61 }
62
63 task_names.push(name.clone());
64
65 let mut fn_params = Vec::new();
67 let mut call_args = Vec::new();
69
70 for param in ¶ms {
71 let param_name = param.name();
72 let type_name = match ¶m.ty.kind {
73 TypeKind::Named(n) => n.clone(),
74 _ => "i32".to_string(), };
76
77 fn_params.push(format!("mut {}: {}", param_name, type_name));
80 call_args.push(param_name);
81 }
82
83 let fn_params_str = fn_params.join(", ");
84 let call_args_str = call_args.join(", ");
85
86 let task_code = format!(
88 "#[embassy_executor::task]\nasync fn {}({}) {{\n{}\n}}",
89 name, fn_params_str, formatted_body
90 );
91 task_definitions.push(task_code);
92
93 task_spawns.push(format!("spawner.spawn({}({})).ok();", name, call_args_str));
95
96 } else {
97 match name.as_str() {
98 "setup" => {
99 setup_body = formatted_body;
100 }
101 "forever" => {
102 loop_body = formatted_body;
103 }
104 _ => {}
105 }
106 }
107 }
108 _ => {
109 transpiler.analyze_mutability(std::slice::from_ref(&expr));
110 let token_stream = transpiler.transpile_expr(&expr)?;
111 let raw_code = token_stream.to_string();
112
113 let mut stmt = raw_code.trim().to_string();
114 if !stmt.ends_with(';') {
115 stmt.push(';');
116 }
117 if stmt.starts_with("let ") {
118 if !stmt.starts_with("let mut ") {
119 stmt = stmt.replace("let ", "let mut ");
120 }
121 } else {
122 stmt = format!("let mut {}", stmt);
123 }
124 variables.push(stmt);
125 }
126 }
127 }
128 }
129
130 Ok(RuchyOutput {
131 setup: setup_body,
132 loop_body,
133 variables,
134 task_definitions,
135 task_names,
136 task_spawns,
137 })
138}
139
140fn apply_async_delay_replacement(input: String) -> String {
141 let search = "delay.delay_millis(";
142 let replacement_start = "Timer::after(Duration::from_millis(";
143
144 let mut result = String::with_capacity(input.len());
145 let mut last_pos = 0;
146
147 while let Some(start_idx) = input[last_pos..].find(search) {
148 let abs_start = last_pos + start_idx;
149 result.push_str(&input[last_pos..abs_start]);
150 result.push_str(replacement_start);
151
152 let args_start = abs_start + search.len();
153 let mut paren_balance = 1;
154 let mut end_idx = args_start;
155
156 for (i, c) in input[args_start..].char_indices() {
157 if c == '(' { paren_balance += 1; }
158 else if c == ')' { paren_balance -= 1; }
159
160 if paren_balance == 0 {
161 end_idx = args_start + i;
162 break;
163 }
164 }
165
166 result.push_str(&input[args_start..end_idx]);
167 result.push_str(")).await");
168 last_pos = end_idx + 1;
169 }
170
171 result.push_str(&input[last_pos..]);
172 result
173}
174
175fn format_rust_code(input: &str, indent_level: usize, enable_async: bool) -> String {
176 let indent = " ".repeat(indent_level);
177 let mut content = input.trim();
178 while content.starts_with('{') && content.ends_with('}') {
179 content = content[1..content.len() - 1].trim();
180 }
181
182 let mut cleaned = content.to_string();
184 cleaned = cleaned.replace(" . ", ".");
185 cleaned = cleaned.replace(" (", "(");
186 cleaned = cleaned.replace(" )", ")");
187 cleaned = cleaned.replace(" ;", ";");
188 cleaned = cleaned.replace(" !", "!");
189 cleaned = cleaned.replace(" :: ", "::");
190
191 if enable_async {
192 cleaned = apply_async_delay_replacement(cleaned);
193 }
194
195 let mut formatted = String::new();
196 let statements: Vec<&str> = cleaned
197 .split(';')
198 .map(|s| s.trim())
199 .filter(|s| !s.is_empty())
200 .collect();
201
202 for (i, stmt) in statements.iter().enumerate() {
203 let final_stmt = stmt.to_string();
204
205 if final_stmt.starts_with("{") && final_stmt.ends_with("}") {
207 }
212
213 if i == 0 {
214 formatted.push_str(&format!("{};", final_stmt));
215 } else {
216 formatted.push_str(&format!("\n{}{};", indent, final_stmt));
217 }
218 }
219
220 formatted
221}
222