/home/noah/src/ruchy/src/backend/transpiler/actors.rs
Line | Count | Source |
1 | | //! Actor system transpilation |
2 | | |
3 | | #![allow(clippy::missing_errors_doc)] |
4 | | #![allow(clippy::wildcard_imports)] |
5 | | |
6 | | use super::*; |
7 | | use crate::frontend::ast::{ActorHandler, StructField}; |
8 | | use anyhow::Result; |
9 | | use proc_macro2::TokenStream; |
10 | | use quote::{format_ident, quote}; |
11 | | |
12 | | impl Transpiler { |
13 | | /// Transpiles actor definitions |
14 | 0 | pub fn transpile_actor( |
15 | 0 | &self, |
16 | 0 | name: &str, |
17 | 0 | state: &[StructField], |
18 | 0 | handlers: &[ActorHandler], |
19 | 0 | ) -> Result<TokenStream> { |
20 | 0 | let actor_name = format_ident!("{}", name); |
21 | 0 | let message_enum_name = format_ident!("{}Message", name); |
22 | | |
23 | | // Generate state fields |
24 | 0 | let state_fields: Vec<TokenStream> = state |
25 | 0 | .iter() |
26 | 0 | .map(|field| { |
27 | 0 | let field_name = format_ident!("{}", field.name); |
28 | 0 | let field_type = self |
29 | 0 | .transpile_type(&field.ty) |
30 | 0 | .unwrap_or_else(|_| quote! { _ }); |
31 | 0 | quote! { #field_name: #field_type } |
32 | 0 | }) |
33 | 0 | .collect(); |
34 | | |
35 | | // Generate message enum variants |
36 | 0 | let mut message_variants = Vec::new(); |
37 | 0 | let mut handler_arms = Vec::new(); |
38 | | |
39 | 0 | for handler in handlers { |
40 | 0 | let variant_name = format_ident!("{}", handler.message_type); |
41 | | |
42 | 0 | if handler.params.is_empty() { |
43 | | // Simple message without parameters |
44 | 0 | message_variants.push(quote! { #variant_name }); |
45 | | |
46 | 0 | let body_tokens = self.transpile_expr(&handler.body)?; |
47 | 0 | handler_arms.push(quote! { |
48 | | #message_enum_name::#variant_name => { |
49 | | #body_tokens |
50 | | } |
51 | | }); |
52 | | } else { |
53 | | // Message with parameters |
54 | 0 | let param_types: Vec<TokenStream> = handler |
55 | 0 | .params |
56 | 0 | .iter() |
57 | 0 | .map(|p| { |
58 | 0 | self.transpile_type(&p.ty) |
59 | 0 | .unwrap_or_else(|_| quote! { String }) |
60 | 0 | }) |
61 | 0 | .collect(); |
62 | | |
63 | 0 | if param_types.len() == 1 { |
64 | 0 | message_variants.push(quote! { #variant_name(#(#param_types),*) }); |
65 | | } else { |
66 | 0 | message_variants.push(quote! { #variant_name { #(#param_types),* } }); |
67 | | } |
68 | | |
69 | | // Generate parameter bindings for the handler |
70 | 0 | let param_names: Vec<_> = handler |
71 | 0 | .params |
72 | 0 | .iter() |
73 | 0 | .map(|p| format_ident!("{}", p.name())) |
74 | 0 | .collect(); |
75 | | |
76 | 0 | let body_tokens = self.transpile_expr(&handler.body)?; |
77 | | |
78 | 0 | if param_names.len() == 1 { |
79 | 0 | let param = ¶m_names[0]; |
80 | 0 | handler_arms.push(quote! { |
81 | 0 | #message_enum_name::#variant_name(#param) => { |
82 | 0 | #body_tokens |
83 | 0 | } |
84 | 0 | }); |
85 | 0 | } else { |
86 | 0 | handler_arms.push(quote! { |
87 | | #message_enum_name::#variant_name { #(#param_names),* } => { |
88 | | #body_tokens |
89 | | } |
90 | | }); |
91 | | } |
92 | | } |
93 | | } |
94 | | |
95 | | // Generate the complete actor implementation |
96 | 0 | Ok(quote! { |
97 | | // Message enum |
98 | | #[derive(Debug, Clone)] |
99 | | enum #message_enum_name { |
100 | | #(#message_variants,)* |
101 | | } |
102 | | |
103 | | // Actor struct |
104 | | struct #actor_name { |
105 | | #(#state_fields,)* |
106 | | receiver: tokio::sync::mpsc::Receiver<#message_enum_name>, |
107 | | sender: tokio::sync::mpsc::Sender<#message_enum_name>, |
108 | | } |
109 | | |
110 | | impl #actor_name { |
111 | | fn new() -> Self { |
112 | | let (sender, receiver) = tokio::sync::mpsc::channel(100); |
113 | | Self { |
114 | | #(#state_fields: Default::default(),)* |
115 | | receiver, |
116 | | sender, |
117 | | } |
118 | | } |
119 | | |
120 | | fn sender(&self) -> tokio::sync::mpsc::Sender<#message_enum_name> { |
121 | | self.sender.clone() |
122 | | } |
123 | | |
124 | | async fn run(&mut self) { |
125 | | while let Some(msg) = self.receiver.recv().await { |
126 | | self.handle_message(msg).await; |
127 | | } |
128 | | } |
129 | | |
130 | | async fn handle_message(&mut self, msg: #message_enum_name) { |
131 | | match msg { |
132 | | #(#handler_arms)* |
133 | | } |
134 | | } |
135 | | } |
136 | | }) |
137 | 0 | } |
138 | | |
139 | | /// Transpiles send operations (actor ! message) |
140 | 0 | pub fn transpile_send(&self, actor: &Expr, message: &Expr) -> Result<TokenStream> { |
141 | 0 | let actor_tokens = self.transpile_expr(actor)?; |
142 | 0 | let message_tokens = self.transpile_expr(message)?; |
143 | | |
144 | 0 | Ok(quote! { |
145 | 0 | #actor_tokens.send(#message_tokens).await |
146 | 0 | }) |
147 | 0 | } |
148 | | |
149 | | /// Transpiles ask operations (actor ? message) |
150 | 0 | pub fn transpile_ask( |
151 | 0 | &self, |
152 | 0 | actor: &Expr, |
153 | 0 | message: &Expr, |
154 | 0 | timeout: Option<&Expr>, |
155 | 0 | ) -> Result<TokenStream> { |
156 | 0 | let actor_tokens = self.transpile_expr(actor)?; |
157 | 0 | let message_tokens = self.transpile_expr(message)?; |
158 | | |
159 | 0 | if let Some(timeout_expr) = timeout { |
160 | 0 | let timeout_tokens = self.transpile_expr(timeout_expr)?; |
161 | 0 | Ok(quote! { |
162 | 0 | #actor_tokens.ask(#message_tokens, #timeout_tokens).await |
163 | 0 | }) |
164 | | } else { |
165 | | // Default timeout of 5 seconds |
166 | 0 | Ok(quote! { |
167 | 0 | #actor_tokens.ask(#message_tokens, std::time::Duration::from_secs(5)).await |
168 | 0 | }) |
169 | | } |
170 | 0 | } |
171 | | |
172 | | /// Transpiles command execution |
173 | 0 | pub fn transpile_command( |
174 | 0 | &self, |
175 | 0 | program: &str, |
176 | 0 | args: &[String], |
177 | 0 | _env: &[(String, String)], |
178 | 0 | _working_dir: &Option<String>, |
179 | 0 | ) -> Result<TokenStream> { |
180 | 0 | let prog_str = program; |
181 | 0 | let args_tokens: Vec<_> = args.iter().map(|arg| quote! { #arg }).collect(); |
182 | | |
183 | 0 | Ok(quote! { |
184 | | std::process::Command::new(#prog_str) |
185 | | .args(&[#(#args_tokens),*]) |
186 | | .output() |
187 | | .expect("Failed to execute command") |
188 | | }) |
189 | 0 | } |
190 | | } |