do_with_in_internal_macros/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3#[macro_use] extern crate quote;
4extern crate proc_macro2;
5
6use proc_macro::{TokenStream, TokenTree};
7use proc_macro2::TokenTree as TokenTree2;
8use proc_macro2::TokenStream as TokenStream2;
9use quote::quote;
10use quote::ToTokens;
11use syn::{parse, Attribute, PathSegment, Result, Token};
12use syn::parse::{Parse, ParseStream, Parser, Peek};
13use syn::spanned::Spanned;
14use syn::{Expr, Ident, Type, Visibility};
15use syn::punctuated::Punctuated;
16use syn::parenthesized;
17use syn::token::Token;
18use syn::buffer::Cursor;
19
20use std::marker::PhantomData;
21
22use std::collections::HashMap;
23use std::fmt::format;
24
25use do_with_in_base::*;
26
27struct Fatuous {
28  fat: TokenStream,
29}
30
31impl Parse for Fatuous {
32  fn parse(input: ParseStream) -> Result<Self> {
33    let mut fat = TokenStream2::new();
34    input.step(|cursor| {
35      let mut rest = *cursor;
36      while let Some((tt, next)) = rest.token_tree() {
37        fat.extend(TokenStream2::from(tt).into_iter());
38        rest = next;
39      }
40      Ok(((), rest))
41    });
42    Ok(Fatuous { fat: fat.into() })
43  }
44}
45
46#[cfg(feature = "doc-kludge")]
47macro_rules! bleh {
48  () => {
49    ""
50  }
51}
52
53#[cfg(not(feature = "doc-kludge"))]
54macro_rules! bleh {
55  () => {
56    concat!(
57"This is the proc_macro most users of this crate will use.\n",
58"\n",
59" There is front matter, which can define the sigil and escaping style. Escaping doesn't actually do anything yet though.\n",
60" Then `do`, then after that is where the metaprogramming can happen.\n",
61"\n",
62" In the metaprogramming section, variables are identifiers with a sigil prepended. You can create and assign to them with `let` and `var` handlers.\n",
63" Numbers with a sigil prepended are special variables that can be set inside a handler; you cannot assign to them with `let` or `var`.\n",
64" Brackets with a sigil prepended start a handler invocation; the handler invoked will be the first token inside the brackets, which must be an identifier.\n",
65"\n",
66" For example, in the following code the sigil is `$`, `$correction_factor` is a normal variable, `$1`, `$2`, and `$3` are special variables set inside the `blah` handler,\n",
67" and `$(let ...)`, `$(mk ...)` and `$(blah ...)` are all handlers.\n",
68"\n",
69" ```rust\n",
70" # use do_with_in_internal_macros::do_with_in;\n",
71" # fn main() {\n",
72" do_with_in!{\n",
73"    sigil: $\n",
74"    do\n",
75"    $(let correction_factor = {(-1)})\n",
76"    $(mk blah\n",
77"        $1 = $2 + $3 + $correction_factor;)\n",
78"    $(blah {let mut d} 3 4)\n",
79"    d += 1;\n",
80"    let correction_factor = $correction_factor;\n",
81"  };\n",
82"  assert_eq!(d, 8 + correction_factor);\n",
83" # }\n",
84" ```\n",
85"\n",
86)
87  }
88}
89
90#[doc = bleh!()]
91#[proc_macro]
92pub fn do_with_in(t: TokenStream) -> TokenStream {
93  //let s = proc_macro::Span::call_site();
94  //let f = s.source_file();
95  let f = file!();
96  let tempt: TokenStream2 = t.into();
97  let tt = quote!{
98    file: #f
99    #tempt
100  };
101  do_with_in_internal(tt).into()
102}
103
104
105#[proc_macro]
106pub fn do_with_in_explicit_internal(t: TokenStream) -> TokenStream {
107  let t_new: TokenStream2 = t.into();
108  quote! {
109    //let t = quote! {
110    //  #t_new
111    //};
112    do_with_in_explicit(c, v, #t_new).into()
113  }.into()
114}
115
116// has_prelude=
117//  true  =>
118//  false =>
119// postlude_marker=
120// default default sigil is $
121// default_sigil=
122// sigil= // <- this also means you can't write 'sigil' in the prelude
123// by default, variables = Variables::default() + any modifications from handlers= and no_interp= and with_interp=, but if variables= is specified then you can't use any of those
124// Maybe
125//   variables=$expr -> let mut v = $expr;
126//   variables+=$expr -> let mut v = Variables::default(); let add = $expr; v.handlers += add.handlers; v.with_interp += add.with_interp; v.no_interp += add.with_interp;
127
128#[proc_macro_attribute]
129pub fn do_with_in_izer(attr: TokenStream, inner: TokenStream) -> TokenStream {
130  let mut has_prelude = true;
131  let mut postlude_marker = quote!{DoMarker};
132  let default_default = quote!{Sigil::Dollar};
133  let mut default_sigil = default_default.clone();
134  let mut sigil = default_sigil.clone();
135  let a: TokenStream2 = attr.into();
136  // Check for
137  let b: TokenStream2 = inner.into();
138  // default_sigil=, sigil=, has_prelude=, postlude_marker=, 
139  let mut b_it = b.into_iter();
140  b_it.next(); // Check for 'pub'
141  b_it.next(); // Check for 'fn'
142  let token = b_it.next();
143  if let Some(TokenTree2::Ident(name)) = token.clone() {
144    let real_name = proc_macro2::Ident::new(&format!{"{}!",name.to_string()}, proc_macro2::Span::call_site());
145    let func_arg_name = match b_it.next() {
146      Some(TokenTree2::Group(grp)) => {
147        if let Some(TokenTree2::Ident(it)) = grp.stream().into_iter().next() {
148          it
149        } else {
150          return quote!{compile_error!{ "Expected an argument list." }}.into()
151        }
152      },
153      Some(x) => {
154        let msg = format!("Expected a function argument list, got {}.", x);
155        return quote!{compile_error!{ #msg }}.into();
156      },
157      None => {
158        return quote!{compile_error!{ "Missing function for do_with_in_izer to be applied to." }}.into();
159      },
160    };
161    let mut body = TokenStream2::new();
162    body.extend(b_it);
163    //let c = ...;
164    return quote! {
165      #[proc_macro]
166      pub fn #real_name(t: TokenStream) -> TokenStream {
167        let tt: TokenStream2 = t.into();
168        // Create c and v
169        // let mut c = ...
170        // 
171        // let mut v = ...
172        let #func_arg_name = do_with_in_base::do_with_in_explicit(c, v, tt).<TokenStream2>::into();
173        #body
174      }
175    }.into()
176  } else {
177    let msg = format!("Expected a name for a function, got {:?}.", token);
178    return quote!{compile_error!{ #msg }}.into();
179  }
180}
181
182/*
183
184make output tokenstream2
185Make HashMap ToTokens if K, V are ToTokens
186Make fn thing ToTokens through... ways?
187add them to output as c=..., v=...
188add ⌜quote!{do_with_in_explicit_internal!(#ident)}
189
190let out = TokenStream2::new();
191let kv_tok   = k.to_tokens();
192let conf_tok = c.to_tokens();
193out.extend(quote!{
194  {
195    let k = #kv_tok;
196    let c = #conf_tok; 
197    do_with_in_explicit_internal!(#ident)
198  }
199});
200out
201
202
203 */
204
205