1#![feature(proc_macro_diagnostic)]
2
3use proc_macro2::Span;
4
5#[macro_use]
6mod shared_array;
7use shared_array::*;
8
9#[macro_use]
10mod shared_map;
11use shared_map::*;
12
13mod symbol;
14use symbol::*;
15
16mod tree_semantics;
17use syn::spanned::Spanned;
18use tree_semantics::*;
19
20mod processing;
21use processing::*;
22
23use proc_macro::TokenStream;
25use quote::{quote, ToTokens};
27use syn::parse::{Parse, ParseStream, Result};
29use syn::punctuated::Punctuated;
30use syn::token::Comma;
31use syn::{braced, parenthesized, parse_macro_input, Attribute, Expr, FnArg, Generics, Ident, Pat, Path, Stmt, Token, Type, Visibility, WhereClause};
33
34use std::cell::RefCell;
35use std::collections::HashMap;
36use std::hash::Hash;
37use std::ops::Deref;
38use std::rc::{Rc, Weak};
39use std::str::FromStr;
40use by_address::ByAddress;
41
42const DATA: &'static str = "__data__";
44
45const DATA_PREFIX: &'static str = "__data_";
46
47const DATA_VARIANT_FIELD: &'static str = "__variant";
49
50const DATA_VARIANT_PREFIX: &'static str = "__variant_";
52
53const DATA_VARIANT_NO_SUBTYPE: &'static str = "__Nothing";
55
56struct SmTypeTree {
57 smodel_path: proc_macro2::TokenStream,
58 arena_type_name: proc_macro2::TokenStream,
59 data_types: Vec<Rc<SmType>>,
60}
61
62struct SmType {
63 attributes: Vec<Attribute>,
64 visibility: Visibility,
65 name: Ident,
66 inherits: Option<Ident>,
67 fields: Vec<Rc<SmTypeField>>,
68 constructor: Option<SmTypeConstructor>,
69 methods: Vec<Rc<SmTypeMethod>>,
70}
71
72struct SmTypeField {
73 is_ref: bool,
74 name: Ident,
75 type_annotation: Type,
76 default_value: Expr,
77}
78
79enum SmTypeMethodOrConstructor {
80 Method(SmTypeMethod),
81 Constructor(SmTypeConstructor),
82}
83
84struct SmTypeConstructor {
85 attributes: Vec<Attribute>,
86 visibility: Visibility,
87 generics: Generics,
88 name: Ident,
89 inputs: Punctuated<FnArg, Comma>,
90 super_arguments: Punctuated<Expr, Comma>,
91 statements: Vec<Stmt>,
92}
93
94struct SmTypeMethod {
95 attributes: RefCell<Vec<Attribute>>,
96 visibility: Visibility,
97 is_override: bool,
98 name: Ident,
99 generics: Generics,
100 inputs: Punctuated<FnArg, Comma>,
101 result_type: Option<Type>,
102 statements: proc_macro2::TokenStream,
103}
104
105impl Parse for SmTypeTree {
106 fn parse(input: ParseStream) -> Result<Self> {
107 let mut smodel_path: Option<Path> = None;
108 if input.peek(Token![mod]) {
109 input.parse::<Token![mod]>()?;
110 input.parse::<Ident>()?;
111 input.parse::<Token![=]>()?;
112 smodel_path = Some(parse_full_qualified_id(input)?);
113 input.parse::<Token![;]>()?;
114 }
115 let arena_type_name = parse_smtype_arena_type_name(input)?.to_token_stream();
116 let mut data_types = vec![];
117 while !input.is_empty() {
118 data_types.push(Rc::new(input.parse::<SmType>()?));
119 }
120 Ok(Self {
121 smodel_path: smodel_path.map(|p| p.to_token_stream()).unwrap_or(proc_macro2::TokenStream::from_str("::hydroperfox_smodel").unwrap()),
122 arena_type_name,
123 data_types,
124 })
125 }
126}
127
128fn parse_full_qualified_id(input: ParseStream) -> Result<Path> {
129 Ok(Path::parse_mod_style(input)?)
130}
131
132impl Parse for SmType {
133 fn parse(input: ParseStream) -> Result<Self> {
134 let attributes = Attribute::parse_outer(input)?;
135 let visibility = input.parse::<Visibility>()?;
136
137 input.parse::<Token![struct]>()?;
138
139 let name = input.parse::<Ident>()?;
140 let name_str = name.to_string();
141
142 let mut inherits: Option<Ident> = None;
144 if input.peek(Token![:]) {
145 input.parse::<Token![:]>()?;
146 inherits = Some(input.parse::<Ident>()?);
147 }
148
149 let mut fields: Vec<Rc<SmTypeField>> = vec![];
150 let mut constructor: Option<SmTypeConstructor> = None;
151 let mut methods: Vec<Rc<SmTypeMethod>> = vec![];
152 let braced_content;
153 let _ = braced!(braced_content in input);
154
155 while !braced_content.is_empty() {
156 if braced_content.peek(Token![let]) {
157 fields.push(Rc::new(parse_smtype_field(&braced_content)?));
158 } else {
159 match parse_smtype_method(&braced_content, &name_str)? {
160 SmTypeMethodOrConstructor::Constructor(ctor) => {
161 constructor = Some(ctor);
162 },
163 SmTypeMethodOrConstructor::Method(m) => {
164 methods.push(Rc::new(m));
165 },
166 }
167 }
168 }
169
170 Ok(Self {
171 attributes,
172 visibility,
173 name,
174 inherits,
175 fields,
176 constructor,
177 methods,
178 })
179 }
180}
181
182fn parse_smtype_field(input: ParseStream) -> Result<SmTypeField> {
183 input.parse::<Token![let]>()?;
184 let is_ref = if input.peek(Token![ref]) {
185 input.parse::<Token![ref]>()?;
186 true
187 } else {
188 false
189 };
190 let name = input.parse::<Ident>()?;
191 input.parse::<Token![:]>()?;
192 let type_annotation = input.parse::<Type>()?;
193 input.parse::<Token![=]>()?;
194 let default_value = input.parse::<Expr>()?;
195 input.parse::<Token![;]>()?;
196
197 Ok(SmTypeField {
198 is_ref,
199 name,
200 type_annotation,
201 default_value,
202 })
203}
204
205fn parse_smtype_method(input: ParseStream, smtype_name: &str) -> Result<SmTypeMethodOrConstructor> {
206 let attributes = Attribute::parse_outer(input)?;
207 let visibility = input.parse::<Visibility>()?;
208 let is_override = if input.peek(Token![override]) {
209 input.parse::<Token![override]>()?;
210 true
211 } else {
212 false
213 };
214 input.parse::<Token![fn]>()?;
215 let mut is_constructor = false;
216 let id = input.parse::<Ident>()?;
217 if !is_override && id.to_string() == smtype_name {
218 is_constructor = true;
220 }
221 let mut generics = input.parse::<Generics>()?;
222
223 let parens_content;
224 parenthesized!(parens_content in input);
225 let inputs = parens_content.parse_terminated(FnArg::parse, Comma)?;
226
227 let result_type: Option<Type> = if !is_constructor && input.peek(Token![->]) {
228 input.parse::<Token![->]>()?;
229 Some(input.parse::<Type>()?)
230 } else {
231 None
232 };
233
234 generics.where_clause = if input.peek(Token![where]) { Some(input.parse::<WhereClause>()?) } else { None };
235
236 let braced_content;
237 let _ = braced!(braced_content in input);
238
239 if !is_constructor {
240 let statements = braced_content.parse::<proc_macro2::TokenStream>()?;
241 return Ok(SmTypeMethodOrConstructor::Method(SmTypeMethod {
242 attributes: RefCell::new(attributes),
243 visibility,
244 is_override,
245 name: id,
246 generics,
247 inputs,
248 result_type,
249 statements,
250 }));
251 }
252
253 braced_content.parse::<Token![super]>()?;
254
255 let paren_content;
256 let _ = parenthesized!(paren_content in braced_content);
257 let super_arguments = paren_content.parse_terminated(Expr::parse, Comma)?;
258 braced_content.parse::<Token![;]>()?;
259
260 let mut statements = vec![];
261 while !braced_content.is_empty() {
262 statements.push(braced_content.parse::<Stmt>()?);
263 }
264
265 Ok(SmTypeMethodOrConstructor::Constructor(SmTypeConstructor {
266 attributes,
267 visibility,
268 generics,
269 name: id,
270 inputs,
271 super_arguments,
272 statements,
273 }))
274}
275
276fn parse_smtype_arena_type_name(input: ParseStream) -> Result<Path> {
277 input.parse::<Token![type]>()?;
278 let id = input.parse::<Ident>()?;
279 if id.to_string() != "Arena" {
280 id.span().unwrap().error("Identifier must be equals \"Arena\"").emit();
281 }
282 input.parse::<Token![=]>()?;
283 let path = Path::parse_mod_style(input)?;
284 input.parse::<Token![;]>()?;
285 Ok(path)
286}
287
288#[proc_macro]
289pub fn smodel(input: TokenStream) -> TokenStream {
290 let SmTypeTree {
291 smodel_path, arena_type_name, data_types
292 } = parse_macro_input!(input as SmTypeTree);
293
294 let mut host = SModelHost::new();
295
296 if data_types.is_empty() {
301 panic!("There must be at least one data type.");
302 }
303
304 if data_types[0].inherits.is_some() {
307 data_types[0].name.span().unwrap().error("First data type must inherit no base.").emit();
308 return TokenStream::new();
309 }
310 let base_smtype_data_name = Ident::new(&(DATA_PREFIX.to_string() + &data_types[0].name.to_string()), Span::call_site());
311
312 for m in data_types[1..].iter() {
315 if m.inherits.is_none() {
316 m.name.span().unwrap().error("Data type must inherit a base.").emit();
317 return TokenStream::new();
318 }
319 }
320
321 let data_id = Ident::new(DATA, Span::call_site());
324
325 host.output.extend::<TokenStream>(quote! {
327 pub type #arena_type_name = #smodel_path::Arena<#data_id::#base_smtype_data_name>;
328 }.try_into().unwrap());
329
330 for smtype_node in data_types.iter() {
332 if !ProcessingStep2().exec(&mut host, smtype_node) {
333 return TokenStream::new();
334 }
335 }
336
337 for smtype_node in data_types.iter() {
339 let Some(smtype) = host.semantics.get(smtype_node) else {
340 continue;
341 };
342
343 let asc_smtype_list = smtype.asc_smtype_list();
344 let mut field_output = proc_macro2::TokenStream::new();
345 let smtype_name = smtype.name();
346
347 let mut base_accessor = "self.0".to_owned();
356 let mut m1 = smtype.clone();
357 while let Some(m2) = m1.inherits() {
358 base_accessor.push_str(".0");
359 m1 = m2;
360 }
361
362 for field in smtype_node.fields.iter() {
364 if !ProcessingStep3_2().exec(&mut host, &smtype, field, &base_accessor, &asc_smtype_list, &mut field_output) {
365 return TokenStream::new();
366 }
367 }
368
369 let subtype_enum = Ident::new(&(DATA_VARIANT_PREFIX.to_owned() + &smtype_name), Span::call_site());
372 let data_variant_field_id = Ident::new(DATA_VARIANT_FIELD, Span::call_site());
373 field_output.extend(quote! {
374 pub #data_variant_field_id: #subtype_enum,
375 });
376
377 let mut variants: Vec<proc_macro2::TokenStream> = vec![];
379 for subtype in smtype.subtypes().iter() {
380 let sn = DATA_PREFIX.to_owned() + &subtype.name();
381 variants.push(proc_macro2::TokenStream::from_str(&format!("{sn}(::std::rc::Rc<{sn}>)")).unwrap());
382 }
383 let data_variant_no_subtype = Ident::new(DATA_VARIANT_NO_SUBTYPE, Span::call_site());
384 variants.push(data_variant_no_subtype.to_token_stream());
385 host.data_output.extend(quote! {
386 pub enum #subtype_enum {
387 #(#variants),*
388 }
389 });
390
391 let smtype_data_id = Ident::new(&format!("{DATA_PREFIX}{}", smtype_name), Span::call_site());
392
393 host.data_output.extend(quote! {
396 pub struct #smtype_data_id {
397 #field_output
398 }
399 });
400
401 ProcessingStep3_6().exec(&mut host, &smtype_node, &smtype, &base_accessor, &smodel_path);
403
404 ProcessingStep3_7().exec(&mut host, smtype_node.constructor.as_ref(), &smtype, &asc_smtype_list, &arena_type_name.to_string());
406
407 for method in smtype_node.methods.iter() {
409 if !ProcessingStep3_8().exec(&mut host, method, &smtype) {
410 return TokenStream::new();
411 }
412 }
413 }
414
415 for smtype_node in data_types.iter() {
417 let Some(smtype) = host.semantics.get(smtype_node) else {
418 continue;
419 };
420
421 let smtype_name = smtype.name();
422 let smtype_name_id = Ident::new(&smtype_name, Span::call_site());
423
424 for method in smtype_node.methods.iter() {
426 ProcessingStep4_1().exec(&mut host, method, &smtype);
427 }
428
429 smtype.method_output().borrow_mut().extend(quote! {
432 pub fn to<T: TryFrom<#smtype_name_id, Error = #smodel_path::SModelError>>(&self) -> Result<T, #smodel_path::SModelError> {
433 T::try_from(self.clone())
434 }
435 pub fn is<T: TryFrom<#smtype_name_id, Error = #smodel_path::SModelError>>(&self) -> bool {
436 T::try_from(self.clone()).is_ok()
437 }
438 });
439
440 let method_output = smtype.method_output().borrow().clone();
441
442 host.output.extend::<TokenStream>(quote! {
444 impl #smtype_name_id {
445 #method_output
446 }
447 }.try_into().unwrap());
448 }
449
450 let data_output = host.data_output;
451
452 host.output.extend::<TokenStream>(quote! {
454 #[allow(non_camel_case_types, non_snake_case)]
455 mod #data_id {
456 use super::*;
457
458 #data_output
459 }
460 }.try_into().unwrap());
461
462 host.output
464}
465
466fn convert_function_input_to_arguments(input: &Punctuated<FnArg, Comma>) -> Punctuated<proc_macro2::TokenStream, Comma> {
467 let mut out = Punctuated::<proc_macro2::TokenStream, Comma>::new();
468 for arg in input.iter() {
469 if let FnArg::Receiver(_) = arg {
470 arg.span().unwrap().error("Unexpected receiver.").emit();
471 continue;
472 } else {
473 let FnArg::Typed(pt) = arg else {
474 panic!();
475 };
476 let Pat::Ident(id) = pt.pat.as_ref() else {
477 pt.pat.span().unwrap().error("Pattern must be an identifier.").emit();
478 continue;
479 };
480 out.push(id.to_token_stream());
481 }
482 }
483 out
484}