derive_deftly_macros/
dbg_allkw.rs

1use super::framework::*;
2
3use std::fmt::Error as E;
4use std::fmt::Result as R;
5use std::fmt::Write;
6
7struct Out<'c> {
8    out: String,
9    subset_only: Option<&'c WithinVariant<'c>>,
10}
11
12impl Write for Out<'_> {
13    fn write_str(&mut self, s: &str) -> fmt::Result {
14        self.out.write_str(s)
15    }
16}
17
18pub fn dump(ctx: &Context) {
19    let w = (|| {
20        let out = String::new();
21        let subset_only = match ctx.within_loop {
22            WithinLoop::None => None,
23            WithinLoop::When | WithinLoop::Body => {
24                Some(ctx.variant.expect("within loop, but not variant!"))
25            }
26        };
27        let mut w = Out { out, subset_only };
28
29        let description =
30            format!("derive-deftly expansions dump {}", ctx.display_for_dbg());
31
32        writeln!(w, "---------- {} (start) ----------", description)?;
33        dump_whole(&mut w, ctx)?;
34        writeln!(w, "---------- {} (end) ----------", description)?;
35
36        Ok::<_, E>(w.out)
37    })()
38    .expect("write to String failed");
39
40    eprint!("{}", w);
41}
42
43fn template_result(ctx: &Context, templ: TokenStream) -> String {
44    let parser = |input: &ParseBuffer<'_>| Template::parse(input);
45    let templ: Template<TokenAccumulator> =
46        parser.parse2(templ).expect("failed to parse own template");
47    let result = (|| {
48        let mut output = TokenAccumulator::new();
49        templ.expand(ctx.as_general(), &mut output);
50        output.tokens()
51    })();
52    match result {
53        Ok(result) => result.to_string(),
54        Err(e) => format!("<error: {}>", e),
55    }
56}
57
58fn dump_any_one(
59    w: &mut Out,
60    ctx: &Context,
61    show_templ: TokenStream,
62    show_op: &str,
63    make_real_templ: &dyn Fn(TokenStream) -> TokenStream,
64) -> R {
65    let show_templ_string = {
66        let mut s = show_templ.to_string();
67        if let Some(inner) = {
68            s.strip_prefix("$ {")
69                .or_else(|| s.strip_prefix("${"))
70                .and_then(|s| s.strip_suffix('}'))
71        } {
72            s = format!("${{{}}}", inner.trim());
73        }
74        s
75    };
76    let lh = format!("{:12} {}", show_templ_string, show_op);
77    let templ = make_real_templ(show_templ);
78    writeln!(w, "        {:16} {}", lh, template_result(ctx, templ))?;
79    Ok(())
80}
81
82fn dump_expand_one(w: &mut Out, ctx: &Context, templ: TokenStream) -> R {
83    dump_any_one(w, ctx, templ, "=>", &|t| t)
84}
85
86fn dump_bool_one(w: &mut Out, ctx: &Context, templ: TokenStream) -> R {
87    let make_real = |templ| quote! { ${if #templ { true } else { false }} };
88    dump_any_one(w, ctx, templ, "=", &make_real)
89}
90
91macro_rules! expand { { $w_ctx:expr, $($t:tt)* } => {
92    dump_expand_one($w_ctx.0, $w_ctx.1, quote!{ $($t)* })?;
93} }
94macro_rules! bool { { $w_ctx:expr, $($t:tt)* } => {
95    dump_bool_one($w_ctx.0, $w_ctx.1, quote!{ $($t)* })?;
96} }
97
98fn dump_whole(mut w: &mut Out, ctx: &Context) -> R {
99    writeln!(w, "top-level:")?;
100    let c = (&mut w, ctx);
101
102    expand! { c, $tname }
103    expand! { c, $ttype }
104    expand! { c, $tvis }
105    expand! { c, $tgens }
106    expand! { c, $tgnames }
107    expand! { c, $twheres }
108    expand! { c, $tdeftype }
109    expand! { c, $tdefgens }
110    expand! { c, $tdefkwd }
111    expand! { c, ${tdefvariants VARIANTS..} }
112
113    bool! { c, is_struct }
114    bool! { c, is_enum }
115    bool! { c, is_union }
116    bool! { c, tvis }
117    bool! { c, tgens }
118
119    expand! { c, $tattrs }
120
121    // Don't debug dump these.  But list them here, so that
122    // check-keywords-documented is happy.  (That is nicer than
123    // using the check-keywords-documented exception table.)
124    if false {
125        // Perhaps we should search attributes and dump what would work
126        expand! { c, $tmeta }
127        expand! { c, $vmeta }
128        expand! { c, $fmeta }
129        bool! { c, tmeta }
130        bool! { c, vmeta }
131        bool! { c, fmeta }
132        // Too complex to demonstrate
133        expand! { c, $paste }
134        expand! { c, $paste_spanned }
135        expand! { c, $concat }
136        // Too subtle to demonstrate
137        expand! { c, $crate }
138        // Recursive, would be silly
139        expand! { c, $dbg_all_keywords }
140        // Would throw an error if we expanded it
141        expand! { c, $error }
142        // Control flow, can't sensibly be dumped
143        expand! { c, $when }
144        expand! { c, $if }
145        expand! { c, $select1 }
146        expand! { c, $define }
147        expand! { c, $defcond }
148        expand! { c, $ignore }
149        expand! { c, $dbg }
150        bool! { c, not }
151        bool! { c, all }
152        bool! { c, any }
153        // Requires input arguments to do anything
154        bool! { c, dbg }
155        bool! { c, is_empty }
156        bool! { c, approx_equal }
157        // Vacuous
158        bool! { c, true }
159        bool! { c, false }
160    }
161
162    if let Some(wv) = w.subset_only {
163        dump_variant(w, ctx, wv)?;
164        dump_user_defined(w, ctx)?;
165    } else {
166        WithinVariant::for_each(ctx, |ctx, wv| dump_variant(w, ctx, wv))?;
167    }
168
169    Ok(())
170}
171
172fn variant_heading(w: &mut Out, wv: &WithinVariant) -> R {
173    match wv.variant {
174        None => write!(w, "value")?,
175        Some(v) => write!(w, "variant {}", v.ident)?,
176    };
177    Ok(())
178}
179
180fn dump_variant(mut w: &mut Out, ctx: &Context, wv: &WithinVariant) -> R {
181    variant_heading(w, wv)?;
182    writeln!(w, ":")?;
183    let c = (&mut w, ctx);
184
185    expand! { c, $vname }
186    expand! { c, $vtype }
187    if false {
188        // beta
189        expand! { c, $vindex }
190    }
191    expand! { c, $vpat }
192    expand! { c, ${vdefbody VNAME FIELDS..} }
193
194    bool! { c, v_is_unit }
195    bool! { c, v_is_tuple }
196    bool! { c, v_is_named }
197
198    expand! { c, $vattrs }
199
200    if let Some(_) = w.subset_only {
201        dump_field(w, ctx, ctx.field)?;
202    } else {
203        WithinField::for_each(ctx, |ctx, wf| dump_field(w, ctx, Some(wf)))?;
204    }
205
206    Ok(())
207}
208
209fn dump_field(mut w: &mut Out, ctx: &Context, wf: Option<&WithinField>) -> R {
210    variant_heading(w, ctx.variant.expect("heading but not variant!"))?;
211    if let Some(wf) = wf {
212        let fname = wf.fname(Span::call_site()).to_token_stream();
213        writeln!(w, ", field {}:", fname)?;
214    } else {
215        writeln!(w, ", no field:")?;
216    }
217    let c = (&mut w, ctx);
218
219    expand! { c, $fname }
220    expand! { c, $ftype }
221    if false {
222        // beta
223        expand! { c, $findex }
224    }
225    expand! { c, $fvis }
226    expand! { c, $fdefvis }
227    expand! { c, $fpatname }
228    expand! { c, $fdefine }
229
230    bool! { c, fvis }
231    bool! { c, fdefvis }
232
233    expand! { c, $fattrs }
234
235    Ok(())
236}
237
238fn dump_user_defined(mut w: &mut Out, ctx: &Context) -> R {
239    // evade macro hygiene
240    let mut c;
241    let mut name;
242
243    macro_rules! print_definitions { {
244        $heading:expr, $B:ty, $body:stmt
245    } => { {
246        let mut set = BTreeSet::new();
247        for def in ctx.defs.defs.iter::<$B>().flatten() {
248            set.insert(&def.name);
249        }
250        if !set.is_empty() {
251            writeln!(w, "{}", $heading)?;
252            c = (&mut w, ctx);
253            for n in set {
254                name = n;
255                $body
256            }
257        }
258    } } }
259
260    print_definitions! {
261        "user-defined expansions:", DefinitionBody,
262        expand! { c, $#name }
263    }
264    print_definitions! {
265        "user-defined conditions:", DefCondBody,
266        bool! { c, #name }
267    }
268
269    Ok(())
270}