mlang_rs/lang/rustgen/
mapping.rs1use heck::{ToSnakeCase, ToUpperCamelCase};
4use proc_macro2::TokenStream;
5use quote::quote;
6
7use crate::lang::ir::{Comment, Enum, Field, Ident, Node, Type};
8
9pub trait CommentMapping {
11 fn to_comment(&self) -> TokenStream;
13}
14
15impl CommentMapping for Comment {
16 fn to_comment(&self) -> TokenStream {
17 format!("/// {0}", self.1).parse().expect("to_comment")
18 }
19}
20
21pub trait IdentMapping {
23 fn to_field_name(&self) -> TokenStream;
25
26 fn to_type_name(&self) -> TokenStream;
28}
29
30impl IdentMapping for Ident {
31 fn to_field_name(&self) -> TokenStream {
32 match self.1.to_snake_case().as_str() {
33 "type" => "r#type".parse().expect("to_field_name"),
34 "in" => "r#in".parse().expect("to_field_name"),
35 "for" => "r#for".parse().expect("to_field_name"),
36 ident => ident.parse().expect("to_field_name"),
37 }
38 }
39
40 fn to_type_name(&self) -> TokenStream {
41 self.1.to_upper_camel_case().parse().expect("to_type_name")
42 }
43}
44
45pub trait TypeMapping {
47 fn to_definition(&self, ty_mod: &TokenStream) -> TokenStream;
49
50 fn to_from_where_clause(
52 &self,
53 ty_mod: &TokenStream,
54 sexpr_mod: &TokenStream,
55 generic_ty: &TokenStream,
56 ) -> TokenStream;
57
58 fn to_from_clause(&self, sexpr_mod: &TokenStream, param: &TokenStream) -> TokenStream;
60}
61
62impl TypeMapping for Type {
63 fn to_definition(&self, ty_mod: &TokenStream) -> TokenStream {
64 match self {
65 Type::Bool(_) => quote! {bool},
66 Type::String(_) => quote! {String},
67 Type::Byte(_) => quote! {i8},
68 Type::Ubyte(_) => quote! {u8},
69 Type::Short(_) => quote! {i16},
70 Type::Ushort(_) => quote! {u16},
71 Type::Int(_) => quote! {i32},
72 Type::Uint(_) => quote! {u32},
73 Type::Long(_) => quote! {i64},
74 Type::Ulong(_) => quote! {u64},
75 Type::Float(_) => quote! {f32},
76 Type::Double(_) => quote! {f64},
77 Type::Data(ident) => {
78 let ident = ident.to_type_name();
79
80 let ty_mod = ty_mod;
81
82 quote! { #ty_mod #ident }
83 }
84 Type::ListOf(component, _) => {
85 let component = component.to_definition(ty_mod);
86
87 quote! { Vec<#component> }
88 }
89 Type::ArrayOf(component, lit_num, _) => {
90 let component = component.to_definition(ty_mod);
91 let num = lit_num.0;
92
93 quote! { [#component;#num] }
94 }
95 }
96 }
97
98 fn to_from_where_clause(
99 &self,
100 ty_mod: &TokenStream,
101 sexpr_mod: &TokenStream,
102 generic_ty: &TokenStream,
103 ) -> TokenStream {
104 match self {
105 Type::Data(ident) => {
106 let ty = ident.to_type_name();
107 let ty_mod = ty_mod;
108 let generic_ty = generic_ty;
109 quote! {
110 #ty_mod #ty : From<#generic_ty>
111 }
112 }
113 Type::ListOf(component, _) => {
114 let ty = component.to_definition(ty_mod);
115 let generic_ty = generic_ty;
116 quote! {
117 #generic_ty: #sexpr_mod MapCollect<#ty>
118 }
119 }
120 Type::Float(_) => {
121 let generic_ty = generic_ty;
122 quote! { #sexpr_mod Number: From<#generic_ty> }
123 }
124 _ => {
125 let ty = self.to_definition(ty_mod);
126 let generic_ty = generic_ty;
127 quote! {
128 #ty : From<#generic_ty>
129 }
130 }
131 }
132 }
133
134 fn to_from_clause(&self, sexpr_mod: &TokenStream, param: &TokenStream) -> TokenStream {
135 let param = param;
136
137 match self {
138 Type::ListOf(_, _) => {
139 quote! {
140 #param.map_collect()
141 }
142 }
143 Type::Float(_) => {
144 quote! { #sexpr_mod Number::from(#param).0 }
145 }
146 _ => {
147 quote! {
148 #param.into()
149 }
150 }
151 }
152 }
153}
154
155pub trait FieldMapping: CommentMapping {
157 fn to_from_clause(&self, sexpr_mod: &TokenStream, param: &TokenStream) -> TokenStream;
159
160 fn to_init_clause(&self, param: &TokenStream) -> TokenStream;
162
163 fn to_type_definition(&self, ty_mod: &TokenStream) -> TokenStream;
165
166 fn to_ident(&self) -> Option<TokenStream>;
168
169 fn to_definition_clause(&self, vis: &TokenStream, ty: &TokenStream) -> TokenStream;
171}
172
173impl<'a> CommentMapping for Field<'a> {
174 fn to_comment(&self) -> TokenStream {
175 self.comments().iter().map(|c| c.to_comment()).collect()
176 }
177}
178
179impl<'a> FieldMapping for Field<'a> {
180 fn to_from_clause(&self, sexpr_mod: &TokenStream, param: &TokenStream) -> TokenStream {
181 let mut param = self.ty().to_from_clause(sexpr_mod, param);
182
183 if self.is_variable() {
184 param = quote! { mlang_rs::rt::opcode::Variable::Constant(#param) };
185 }
186
187 if self.is_option() {
188 param = quote! { Some(#param) };
189 }
190
191 param
192 }
193
194 fn to_init_clause(&self, param: &TokenStream) -> TokenStream {
195 if let Some(ident) = self.to_ident() {
196 quote! { #ident: #param }
197 } else {
198 quote! { #param }
199 }
200 }
201
202 fn to_type_definition(&self, ty_mod: &TokenStream) -> TokenStream {
203 let mut ty = self.ty().to_definition(ty_mod);
204
205 if self.is_variable() {
206 ty = quote! {
207 mlang_rs::rt::opcode::Variable<#ty>
208 };
209 }
210
211 if self.is_option() {
212 ty = quote! { Option<#ty> };
213 }
214
215 ty
216 }
217
218 fn to_definition_clause(&self, vis: &TokenStream, ty: &TokenStream) -> TokenStream {
219 let attrs = if self.is_option() {
220 quote! { #[serde(skip_serializing_if = "Option::is_none")] }
221 } else {
222 quote! {}
223 };
224
225 if let Some(ident) = self.to_ident() {
226 quote! { #attrs #vis #ident: #ty }
227 } else {
228 quote! { #attrs #vis #ty }
229 }
230 }
231
232 fn to_ident(&self) -> Option<TokenStream> {
233 self.ident().map(|ident| ident.to_field_name())
234 }
235}
236
237pub trait ComplexTypeMapping: CommentMapping {
239 fn to_ident(&self) -> TokenStream;
241
242 fn to_semi_token(&self) -> TokenStream;
244
245 fn to_struct_body(&self, fields: impl AsRef<[TokenStream]>) -> TokenStream;
247}
248
249impl CommentMapping for Node {
250 fn to_comment(&self) -> TokenStream {
251 self.comments.iter().map(|c| c.to_comment()).collect()
252 }
253}
254
255impl CommentMapping for Enum {
256 fn to_comment(&self) -> TokenStream {
257 self.comments.iter().map(|c| c.to_comment()).collect()
258 }
259}
260
261impl ComplexTypeMapping for Node {
262 fn to_ident(&self) -> TokenStream {
263 self.ident.to_type_name()
264 }
265
266 fn to_semi_token(&self) -> TokenStream {
267 if self.is_tuple() {
268 quote! {;}
269 } else {
270 quote! {}
271 }
272 }
273
274 fn to_struct_body(&self, fields: impl AsRef<[TokenStream]>) -> TokenStream {
275 let fields = fields.as_ref();
276
277 if fields.is_empty() {
278 return quote! {};
279 }
280
281 if self.is_tuple() {
282 quote! { (#(#fields),*) }
283 } else {
284 quote! { {#(#fields),*} }
285 }
286 }
287}
288
289impl ComplexTypeMapping for Enum {
290 fn to_ident(&self) -> TokenStream {
291 self.ident.to_type_name()
292 }
293
294 fn to_semi_token(&self) -> TokenStream {
295 quote! {}
296 }
297
298 fn to_struct_body(&self, fields: impl AsRef<[TokenStream]>) -> TokenStream {
299 let fields = fields.as_ref();
300
301 assert!(!fields.is_empty(), "enum fields is empty.");
302
303 if fields.is_empty() {
304 return quote! {};
305 }
306
307 quote! { {#(#fields),*} }
308 }
309}