git.delta.rocks / jrsonnet / refs/commits / 7efefe23889c

difftreelog

feature: non-static builtins

Yaroslav Bolyukin2022-03-07parent: #86c60d8.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
1use quote::quote;1use quote::{quote, quote_spanned};
2use syn::{2use syn::{
3 parse_macro_input, FnArg, GenericArgument, ItemFn, Pat, PatType, Path, PathArguments, Type,3 parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,
4 token::Comma, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments, Token,
5 Type,
4};6};
57
6fn is_location_arg(t: &PatType) -> bool {8fn is_location_arg(t: &PatType) -> bool {
7 t.attrs.iter().any(|a| a.path.is_ident("location"))9 t.attrs.iter().any(|a| a.path.is_ident("location"))
8}10}
11fn is_self_arg(t: &PatType) -> bool {
12 t.attrs.iter().any(|a| a.path.is_ident("self"))
13}
914
10trait RetainHad<T> {15trait RetainHad<T> {
11 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;16 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;
45 }50 }
46}51}
52
53struct Field {
54 name: Ident,
55 _colon: Token![:],
56 ty: Type,
57}
58impl Parse for Field {
59 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
60 Ok(Self {
61 name: input.parse()?,
62 _colon: input.parse()?,
63 ty: input.parse()?,
64 })
65 }
66}
67
68mod kw {
69 syn::custom_keyword!(fields);
70}
71
72struct BuiltinAttrs {
73 fields: Vec<Field>,
74}
75impl Parse for BuiltinAttrs {
76 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
77 if input.is_empty() {
78 return Ok(Self { fields: Vec::new() });
79 }
80 input.parse::<kw::fields>()?;
81 let fields;
82 parenthesized!(fields in input);
83 let p = Punctuated::<Field, Comma>::parse_terminated(&fields)?;
84 Ok(Self {
85 fields: p.into_iter().collect(),
86 })
87 }
88}
4789
48#[proc_macro_attribute]90#[proc_macro_attribute]
49pub fn builtin(91pub fn builtin(
50 _attr: proc_macro::TokenStream,92 attr: proc_macro::TokenStream,
51 item: proc_macro::TokenStream,93 item: proc_macro::TokenStream,
52) -> proc_macro::TokenStream {94) -> proc_macro::TokenStream {
53 // syn::ItemFn::parse(input)95 let attrs = parse_macro_input!(attr as BuiltinAttrs);
54 let mut fun: ItemFn = parse_macro_input!(item);96 let mut fun: ItemFn = parse_macro_input!(item);
5597
56 let result = match fun.sig.output {98 let result = match fun.sig.output {
57 syn::ReturnType::Default => panic!("builtin should return something"),99 syn::ReturnType::Default => {
100 return quote_spanned! { fun.sig.span() =>
101 compile_error!("builtins should return something");
102 }
103 .into()
104 }
58 syn::ReturnType::Type(_, ref ty) => ty.clone(),105 syn::ReturnType::Type(_, ref ty) => ty.clone(),
59 };106 };
60107
66 FnArg::Receiver(_) => unreachable!(),113 FnArg::Receiver(_) => unreachable!(),
67 FnArg::Typed(t) => t,114 FnArg::Typed(t) => t,
68 })115 })
69 .filter(|a| !is_location_arg(a))116 .filter(|a| !is_location_arg(a) && !is_self_arg(a))
70 .map(|t| {117 .map(|t| {
71 let ident = match &t.pat as &Pat {118 let ident = match &t.pat as &Pat {
72 Pat::Ident(i) => i.ident.to_string(),119 Pat::Ident(i) => i.ident.to_string(),
73 _ => panic!("only idents supported yet"),120 _ => {
121 return quote_spanned! { t.pat.span() =>
122 compile_error!("args should be plain identifiers")
123 }
124 .into()
125 }
74 };126 };
75 let optional = extract_type_from_option(&t.ty).is_some();127 let optional = extract_type_from_option(&t.ty).is_some();
76 quote! {128 quote! {
91 FnArg::Typed(t) => t,143 FnArg::Typed(t) => t,
92 })144 })
93 .map(|t| {145 .map(|t| {
94 let is_location = t.attrs.retain_had(|a| !a.path.is_ident("location"));146 if t.attrs.retain_had(|a| !a.path.is_ident("location")) {
147 quote! {{
148 loc
149 }}
95 if is_location {150 } else if t.attrs.retain_had(|a| !a.path.is_ident("self")) {
96 quote! {{151 quote! {{
97 loc152 self
98 }}153 }}
99 } else {154 } else {
100 let ident = match &t.pat as &Pat {155 let ident = match &t.pat as &Pat {
101 Pat::Ident(i) => i.ident.to_string(),156 Pat::Ident(i) => i.ident.to_string(),
102 _ => panic!("only idents supported yet"),157 _ => {
158 return quote_spanned! { t.pat.span() =>
159 compile_error!("args should be plain identifiers")
160 }
161 .into()
162 }
103 };163 };
104 let ty = &t.ty;164 let ty = &t.ty;
105 if let Some(opt_ty) = extract_type_from_option(&t.ty) {165 if let Some(opt_ty) = extract_type_from_option(&t.ty) {
127 })187 })
128 .collect::<Vec<_>>();188 .collect::<Vec<_>>();
189
190 let fields = attrs.fields.iter().map(|field| {
191 let name = &field.name;
192 let ty = &field.ty;
193 quote! {
194 pub #name: #ty,
195 }
196 });
129197
130 let name = &fun.sig.ident;198 let name = &fun.sig.ident;
131 let vis = &fun.vis;199 let vis = &fun.vis;
200 let static_ext = if attrs.fields.is_empty() {
201 quote! {
202 impl #name {
203 pub const INST: &'static dyn StaticBuiltin = &#name {};
204 }
205 impl StaticBuiltin for #name {}
206 }
207 } else {
208 quote! {}
209 };
210 let static_derive_copy = if attrs.fields.is_empty() {
211 quote! {, Copy}
212 } else {
213 quote! {}
214 };
215
132 (quote! {216 (quote! {
133 #fun217 #fun
134 #[doc(hidden)]218 #[doc(hidden)]
135 #[allow(non_camel_case_types)]219 #[allow(non_camel_case_types)]
136 #[derive(Clone, Copy, gcmodule::Trace)]220 #[derive(Clone, gcmodule::Trace #static_derive_copy)]
137 #vis struct #name {}221 #vis struct #name {
222 #(#fields)*
223 }
138 const _: () = {224 const _: () = {
139 use ::jrsonnet_evaluator::{225 use ::jrsonnet_evaluator::{
145 #(#params),*231 #(#params),*
146 ];232 ];
147233
148 impl #name {234 #static_ext
149 pub const INST: &'static dyn StaticBuiltin = &#name {};
150 }
151 impl StaticBuiltin for #name {}
152 impl Builtin for #name235 impl Builtin for #name
153 where236 where
154 Self: 'static237 Self: 'static