1use super::framework::*;
4use adviseable::*;
5use modules::ImportedDefinitions;
6
7#[derive(Debug)]
13struct EngineExpandInput {
14 driver: syn::DeriveInput,
15 options: DdOptions,
16 imported_definitions: ImportedDefinitions,
17 template: TopTemplate,
18 template_crate: syn::Path,
19 template_name: Option<syn::Path>,
20 chain_next: Option<ChainNext>,
21 chain_after: TokenStream,
22 accum: TokenStream,
23}
24
25#[derive(Debug)]
26pub struct ChainNext {
27 pub call: TokenStream,
28 pub after_driver: TokenStream,
29}
30
31enum EngineContext {
32 Expand {
33 opcontext_template: OpContext,
34 options: DdOptions,
35 },
36 Final {},
37}
38
39#[derive(Debug)]
40enum EngineInput {
41 Expand(EngineExpandInput),
42 Final(accum::EngineFinalInput),
43 DefinitionViaModules(define::TemplateDefinition),
44 ModuleDefinitionViaModules(modules::ModuleDefinition),
45}
46
47impl Parse for ChainNext {
48 fn parse(input: ParseStream) -> syn::Result<Self> {
49 let call = input.parse::<syn::Path>()?.to_token_stream();
50 let after_driver;
51 let _ = parenthesized!(after_driver in input);
52 let after_driver = after_driver.parse()?;
53 Ok(ChainNext { call, after_driver })
54 }
55}
56
57impl ToTokens for ChainNext {
58 fn to_tokens(&self, out: &mut TokenStream) {
59 let ChainNext { call, after_driver } = self;
60 quote!( #call (#after_driver) ).to_tokens(out);
61 }
62}
63
64fn parse_via_mdoules_invocation(
65 input: ParseStream,
66) -> AdviseableResult<EngineInput> {
67 let chain; let this_args; let prefix_d_escaped;
70 let main;
71 let extra; let _ = bracketed!(chain in input);
74 let _ = braced!(this_args in chain);
75 let mode = syn::Ident::parse_any(&chain)?;
76 let _ = braced!(prefix_d_escaped in input);
77 let _ = braced!(main in input);
78 let _ = parenthesized!(extra in input);
79 let _dollar: Token![$] = extra.parse()?;
80
81 let r = if mode == "define" {
82 let def = parse_unadvised! { main => || {
83 define::TemplateDefinition::parse_from_via_modules(
84 &prefix_d_escaped,
85 main,
86 )
87 } };
88
89 EngineInput::DefinitionViaModules(def)
90 } else if mode == "defmod" {
91 let beta = beta::Enabled::new_for_modules_feature(mode.span())?;
92 let def = parse_unadvised! { main => || {
93 modules::ModuleDefinition::parse_from_via_modules(
94 &prefix_d_escaped,
95 main,
96 beta,
97 )
98 } };
99
100 EngineInput::ModuleDefinitionViaModules(def)
101 } else {
102 return Err(
103 mode.error(format!("unknown engine via_modules mode `{}`", mode))
104 );
105 };
106
107 let _: TokenStream = extra.parse()?;
109 let _: TokenStream = this_args.parse()?;
110 let _: TokenStream = chain.parse()?;
111
112 Ok(AOk(r))
113}
114
115impl ParseAdviseable for EngineInput {
116 fn parse_adviseable(input: ParseStream) -> AdviseableResult<Self> {
117 if input.peek(Ident::peek_any) {
118 let special = syn::Ident::parse_any(input)?;
119 return if special == "via_modules" {
120 parse_via_mdoules_invocation(input)
121 } else {
122 Err(special.error(format!(
123 "unknown engine special expansion request `{}`",
124 special,
125 )))
126 };
127 }
128
129 let driver;
130 let _ = braced!(driver in input);
131 let driver = driver.parse()?;
132
133 let engine_context;
134 if input.peek(syn::token::Bracket) {
135 let tokens;
139 let mut options = DdOptions::default();
140
141 let _ = bracketed!(tokens in input);
142 parse_unadvised! {
143 tokens => || {
144 let oc = OpContext::DriverApplicationPassed;
145 options .parse_update(&tokens, oc)
146 }
147 }
148 engine_context = EngineContext::Expand {
149 opcontext_template: OpContext::TemplateDefinition,
150 options,
151 };
152 } else if input.peek(Token![.]) {
153 let _indicator: Token![.] = input.parse()?;
154 engine_context = EngineContext::Final {};
155 } else {
156 engine_context = EngineContext::Expand {
157 opcontext_template: OpContext::TemplateAdhoc,
158 options: DdOptions::default(),
159 };
160 }
161
162 let future_ignored;
163 let _ = parenthesized!(future_ignored in input);
164 let _: TokenStream = future_ignored.parse()?;
165
166 let r = match engine_context {
167 EngineContext::Expand {
168 opcontext_template,
169 options,
170 } => EngineExpandInput::parse_adviseable_remainder(
171 driver,
172 options,
173 input,
174 opcontext_template,
175 )?
176 .map(EngineInput::Expand),
177 EngineContext::Final {} => {
178 accum::EngineFinalInput::parse_adviseable_remainder(
179 driver, input,
180 )?
181 .map(EngineInput::Final)
182 }
183 };
184 Ok(r)
185 }
186}
187
188impl EngineExpandInput {
189 fn parse_adviseable_remainder(
190 driver: syn::DeriveInput,
191 mut options: DdOptions,
192 input: ParseStream,
193 opcontext_template: OpContext,
194 ) -> AdviseableResult<Self> {
195 let template;
196 let _ = braced!(template in input);
197
198 let template_crate;
199 let template_name;
200 let imported_definitions;
201 {
202 let through_driver;
203 let _ = parenthesized!(through_driver in input);
204 let input = through_driver;
205
206 template_crate = input.parse()?;
207 let _: Token![;] = input.parse()?;
208
209 let tokens;
210 let _ = bracketed!(tokens in input);
211 parse_unadvised! {
212 tokens => || {
213 options.parse_update(&tokens, opcontext_template)
214 }
215 }
216
217 template_name = if input.peek(Token![;]) {
218 None
219 } else {
220 Some(input.parse()?)
221 };
222 let _: Token![;] = input.parse()?;
223
224 imported_definitions = if !input.is_empty() {
225 let imported_definitions;
226 let _ = braced!(imported_definitions in input);
227 imported_definitions.parse()?
228 } else {
229 ImportedDefinitions::default()
230 };
231
232 let _: TokenStream = input.parse()?;
233 }
234
235 let (chain_next, chain_after);
236 {
237 let chain;
238 let _ = bracketed!(chain in input);
239 let input = chain;
240 chain_next = if !input.is_empty() {
241 Some(input.parse()?)
242 } else {
243 None
244 };
245 chain_after = input.parse()?;
246 }
247
248 let accum;
249 let _ = bracketed!(accum in input);
250 let accum = accum.parse()?;
251
252 let _: TokenStream = input.parse()?;
253
254 let template = parse_unadvised! {
255 template => || TopTemplate::parse(
256 template,
257 options.beta_enabled,
258 )
259 };
260
261 Ok(AOk(EngineExpandInput {
262 driver,
263 options,
264 imported_definitions,
265 template,
266 template_crate,
267 template_name,
268 chain_next,
269 chain_after,
270 accum,
271 }))
272 }
273}
274
275impl<'c> Context<'c> {
276 pub fn call<T>(
281 driver: &syn::DeriveInput,
282 template_crate: &syn::Path,
283 template_name: Option<&syn::Path>,
284 f: impl FnOnce(Context) -> syn::Result<T>,
285 ) -> Result<T, syn::Error> {
286 let tmetas = preprocess_attrs(&driver.attrs)?;
287
288 let pvariants_one = |fields| {
289 let pmetas = &tmetas;
290 let pfields = preprocess_fields(fields)?;
291 let pvariant = PreprocessedVariant {
292 fields,
293 pmetas,
294 pfields,
295 };
296 syn::Result::Ok((Some(()), vec![pvariant]))
297 };
298
299 let union_fields;
300 let variants_pmetas: Vec<_>;
301
302 let (variant, pvariants) = match &driver.data {
303 syn::Data::Struct(ds) => pvariants_one(&ds.fields)?,
304 syn::Data::Union(du) => {
305 union_fields = syn::Fields::Named(du.fields.clone());
306 pvariants_one(&union_fields)?
307 }
308 syn::Data::Enum(de) => (None, {
309 variants_pmetas = de
310 .variants
311 .iter()
312 .map(|variant| preprocess_attrs(&variant.attrs))
313 .try_collect()?;
314 izip!(&de.variants, &variants_pmetas)
315 .map(|(variant, pmetas)| {
316 let fields = &variant.fields;
317 let pfields = preprocess_fields(&variant.fields)?;
318 Ok(PreprocessedVariant {
319 fields,
320 pmetas,
321 pfields,
322 })
323 })
324 .collect::<Result<Vec<_>, syn::Error>>()?
325 }),
326 };
327
328 let variant = variant.map(|()| WithinVariant {
331 variant: None, fields: pvariants[0].fields,
333 pmetas: &pvariants[0].pmetas,
334 pfields: &pvariants[0].pfields,
335 index: 0,
336 });
337
338 let ctx = Context {
339 top: &driver,
340 template_crate,
341 template_name,
342 pmetas: &tmetas,
343 field: None,
344 variant: variant.as_ref(),
345 pvariants: &pvariants,
346 defs: DefinitionsContext {
347 defs: Default::default(),
348 nesting_depth: 0,
349 nesting_parent: None,
350 },
351 within_loop: WithinLoop::None,
352 };
353
354 f(ctx)
355 }
356}
357
358impl EngineExpandInput {
359 fn process(self) -> syn::Result<TokenStream> {
360 dprintln!("derive_deftly_engine! crate = {:?}", &self.template_crate);
361
362 let DdOptions {
363 dbg,
364 driver_kind,
365 expect_target,
366 beta_enabled,
367 } = self.options;
369
370 let _: Option<_> = beta_enabled;
372
373 if let Some(exp) = driver_kind {
374 macro_rules! got_kind { { $($kind:ident)* } => {
375 match &self.driver.data {
376 $(
377 syn::Data::$kind(..) => ExpectedDriverKind::$kind,
378 )*
379 }
380 } }
381
382 let got_kind = got_kind!(Struct Enum Union);
383 if got_kind != exp.value {
384 return Err([
385 (exp.span, "expected kind"),
386 (self.driver.span(), "actual kind"),
387 ]
388 .error(format_args!(
389 "template defined for {}, but applied to {}",
390 exp.value, got_kind,
391 )));
392 }
393 }
394
395 let outcome = Context::call(
396 &self.driver,
397 &self.template_crate,
398 self.template_name.as_ref(),
399 |ctx| {
400 let mut output = TokenAccumulator::new();
401 Template::expand_iter(
402 chain!(
403 &self.imported_definitions.elements,
404 &self.template.elements,
405 ),
406 ctx.as_general(),
407 &mut output,
408 );
409 let output = output.tokens()?;
410
411 if dbg {
413 let description = ctx.expansion_description();
414 let dump = format!(
415 concat!(
416 "---------- {} (start) ----------\n",
417 "{}\n",
418 "---------- {} (end) ----------\n",
419 ),
420 &description, &output, &description,
421 );
422 eprint!("{}", dump);
423 }
424
425 let mut output = output;
426 if let Some(target) = expect_target {
427 check::check_expected_target_syntax(
428 &ctx,
429 &mut output,
430 target,
431 );
432 }
433
434 let metas_used = ctx.encode_metas_used();
435
436 Ok((output, metas_used))
437 },
438 );
439
440 let (expanded, metas_used) = match outcome {
441 Ok((expanded, metas_used)) => (Ok(expanded), Ok(metas_used)),
442 Err(e) => (Err(e), Err(())),
443 };
444
445 let chain_call;
446
447 if let Some(ChainNext { call, after_driver }) = &self.chain_next {
448 let driver = &self.driver;
449 let chain_after = &self.chain_after;
450
451 let mut accum = self.accum.to_token_stream();
452 if let Some(name) = &self.template_name {
453 accum.extend(quote!( _name [#name] ));
454 }
455 match &metas_used {
456 Ok(metas_used) => {
457 accum.extend(quote!( _meta_used #metas_used ));
458
459 use meta::FindRecogMetas as _;
460 let mut meta_recog = meta::Recognised::default();
461 self.template.find_recog_metas(&mut meta_recog);
462 accum.extend(quote!( _meta_recog [#meta_recog] ));
463 }
464 Err(()) => {
465 accum.extend(quote!( _error [] ));
466 }
467 }
468
469 chain_call = quote! {
470 #call! {
471 { #driver }
472 #after_driver
473 [ #chain_after ]
474 [ #accum ]
475 }
476 }
477 } else {
478 chain_call = TokenStream::new();
479 };
480
481 let mut out = expanded.unwrap_or_else(|e| e.into_compile_error());
482 dprint_block!(&out, "derive_deftly_engine! expansion output");
483 dprint_block!(&chain_call, "derive_deftly_engine! chain call");
484 out.extend(chain_call);
485
486 Ok(out)
487 }
488}
489
490pub fn derive_deftly_engine_func_macro(
498 input: TokenStream,
499) -> syn::Result<TokenStream> {
500 dprint_block!(&input, "derive_deftly_engine! input");
501 let input: EngineInput = adviseable_parse2(input)?;
502 match input {
503 EngineInput::Expand(i) => i.process(),
504 EngineInput::Final(i) => i.process(),
505 EngineInput::DefinitionViaModules(def) => define::define_template(
506 def,
507 "derive_deftly_engine! output, define_derive_deftly via modules",
508 ),
509 EngineInput::ModuleDefinitionViaModules(def) => {
510 modules::define_module(
511 def,
512 "derive_deftly_engine! output, define module via modules",
513 )
514 }
515 }
516}