syndicate_macros/
lib.rs

1#![feature(proc_macro_span)]
2
3use syndicate::value::IOValue;
4use syndicate::value::NestedValue;
5use syndicate::value::Value;
6use syndicate::value::text::iovalue_from_str;
7
8use proc_macro2::Span;
9use proc_macro2::TokenStream;
10
11use quote::quote;
12
13use std::convert::TryFrom;
14
15use syn::parse_macro_input;
16use syn::ExprLit;
17use syn::Ident;
18use syn::Lit;
19use syn::LitByteStr;
20
21mod dur;
22mod pat;
23mod stx;
24mod val;
25
26use pat::lit;
27
28enum SymbolVariant<'a> {
29    Normal(&'a str),
30    #[allow(dead_code)] // otherwise we get 'warning: field `0` is never read'
31    Binder(&'a str),
32    Substitution(&'a str),
33    Discard,
34}
35
36fn compile_sequence_members(vs: &[IOValue]) -> Vec<TokenStream> {
37    vs.iter().enumerate().map(|(i, f)| {
38        let p = compile_pattern(f);
39        quote!((syndicate::value::Value::from(#i).wrap(), #p))
40    }).collect::<Vec<_>>()
41}
42
43fn analyze_symbol(s: &str, allow_binding_and_substitution: bool) -> SymbolVariant {
44    if !allow_binding_and_substitution {
45        SymbolVariant::Normal(s)
46    } else if s.starts_with("$") {
47        SymbolVariant::Binder(&s[1..])
48    } else if s.starts_with("=") {
49        SymbolVariant::Substitution(&s[1..])
50    } else if s == "_" {
51        SymbolVariant::Discard
52    } else {
53        SymbolVariant::Normal(s)
54    }
55}
56
57struct ValueCompiler {
58    allow_binding_and_substitution: bool,
59}
60
61impl ValueCompiler {
62    fn for_patterns() -> Self {
63        ValueCompiler {
64            allow_binding_and_substitution: false,
65        }
66    }
67
68    fn for_templates() -> Self {
69        ValueCompiler {
70            allow_binding_and_substitution: true,
71        }
72    }
73
74    fn compile(&self, v: &IOValue) -> TokenStream {
75        #[allow(non_snake_case)]
76        let V_: TokenStream = quote!(syndicate::value);
77
78        let walk = |w| self.compile(w);
79
80        match v.value() {
81            Value::Boolean(b) =>
82                quote!(#V_::Value::from(#b).wrap()),
83            Value::Double(d) => {
84                let d = d.0;
85                quote!(#V_::Value::from(#d).wrap())
86            }
87            Value::SignedInteger(i) => {
88                let i = i128::try_from(i).expect("Literal integer out-of-range");
89                quote!(#V_::Value::from(#i).wrap())
90            }
91            Value::String(s) =>
92                quote!(#V_::Value::from(#s).wrap()),
93            Value::ByteString(bs) => {
94                let bs = LitByteStr::new(bs, Span::call_site());
95                quote!(#V_::Value::from(#bs).wrap())
96            }
97            Value::Symbol(s) => match analyze_symbol(&s, self.allow_binding_and_substitution) {
98                SymbolVariant::Normal(s) =>
99                    quote!(#V_::Value::symbol(#s).wrap()),
100                SymbolVariant::Binder(_) |
101                SymbolVariant::Discard =>
102                    panic!("Binding/Discard not supported here"),
103                SymbolVariant::Substitution(s) => {
104                    let i = Ident::new(s, Span::call_site());
105                    quote!(#i)
106                }
107            }
108            Value::Record(r) => {
109                let arity = r.arity();
110                let label = walk(r.label());
111                let fs: Vec<_> = r.fields().iter().map(walk).collect();
112                quote!({
113                    let mut ___r = #V_::Value::record(#label, #arity);
114                    #(___r.fields_vec_mut().push(#fs);)*
115                    ___r.finish().wrap()
116                })
117            }
118            Value::Sequence(vs) => {
119                let vs: Vec<_> = vs.iter().map(walk).collect();
120                quote!(#V_::Value::from(vec![#(#vs),*]).wrap())
121            }
122            Value::Set(vs) => {
123                let vs: Vec<_> = vs.iter().map(walk).collect();
124                quote!({
125                    let mut ___s = #V_::Set::new();
126                    #(___s.insert(#vs);)*
127                    #V_::Value::from(___s).wrap()
128                })
129            }
130            Value::Dictionary(d) => {
131                let members: Vec<_> = d.iter().map(|(k, v)| {
132                    let k = walk(k);
133                    let v = walk(v);
134                    quote!(___d.insert(#k, #v))
135                }).collect();
136                quote!({
137                    let mut ___d = #V_::Map::new();
138                    #(#members;)*
139                    #V_::Value::from(___d).wrap()
140                })
141            }
142            Value::Embedded(_) =>
143                panic!("Embedded values in compile-time Preserves templates not (yet?) supported"),
144        }
145    }
146}
147
148fn compile_pattern(v: &IOValue) -> TokenStream {
149    #[allow(non_snake_case)]
150    let P_: TokenStream = quote!(syndicate::schemas::dataspace_patterns);
151    #[allow(non_snake_case)]
152    let V_: TokenStream = quote!(syndicate::value);
153    #[allow(non_snake_case)]
154    let MapFrom_: TokenStream = quote!(<#V_::Map<_, _>>::from);
155
156    match v.value() {
157        Value::Symbol(s) => match analyze_symbol(&s, true) {
158            SymbolVariant::Binder(_) =>
159                quote!(#P_::Pattern::Bind{ pattern: Box::new(#P_::Pattern::Discard) }),
160            SymbolVariant::Discard =>
161                quote!(#P_::Pattern::Discard),
162            SymbolVariant::Substitution(s) =>
163                lit(Ident::new(s, Span::call_site())),
164            SymbolVariant::Normal(_) =>
165                lit(ValueCompiler::for_patterns().compile(v)),
166        }
167        Value::Record(r) => {
168            match r.label().value().as_symbol() {
169                None => panic!("Record labels in patterns must be symbols"),
170                Some(label) =>
171                    if label.starts_with("$") && r.arity() == 1 {
172                        let nested = compile_pattern(&r.fields()[0]);
173                        quote!(#P_::Pattern::Bind{ pattern: Box::new(#nested) })
174                    } else {
175                        let label_stx = if label.starts_with("=") {
176                            let id = Ident::new(&label[1..], Span::call_site());
177                            quote!(#id)
178                        } else {
179                            quote!(#V_::Value::symbol(#label).wrap())
180                        };
181                        let members = compile_sequence_members(r.fields());
182                        quote!(#P_::Pattern::Group {
183                            type_: Box::new(#P_::GroupType::Rec { label: #label_stx }),
184                            entries: #MapFrom_([#(#members),*]),
185                        })
186                    }
187            }
188        }
189        Value::Sequence(vs) => {
190            let members = compile_sequence_members(vs);
191            quote!(#P_::Pattern::Group {
192                type_: Box::new(#P_::GroupType::Arr),
193                entries: #MapFrom_([#(#members),*]),
194            })
195        }
196        Value::Set(_) =>
197            panic!("Cannot match sets in patterns"),
198        Value::Dictionary(d) => {
199            let members = d.iter().map(|(k, v)| {
200                let k = ValueCompiler::for_patterns().compile(k);
201                let v = compile_pattern(v);
202                quote!((#k, #v))
203            }).collect::<Vec<_>>();
204            quote!(#P_::Pattern::Group {
205                type_: Box::new(#P_::GroupType::Dict),
206                entries: #MapFrom_([#(#members),*]),
207            })
208        }
209        _ => lit(ValueCompiler::for_patterns().compile(v)),
210    }
211}
212
213#[proc_macro]
214pub fn pattern_str(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
215    if let Lit::Str(s) = parse_macro_input!(src as ExprLit).lit {
216        match iovalue_from_str(&s.value()) {
217            Ok(v) => {
218                let e = compile_pattern(&v);
219                // println!("{:#}", &e);
220                return e.into();
221            }
222            Err(_) => (),
223        }
224    }
225
226    panic!("Expected literal string containing the pattern and no more");
227}
228
229#[proc_macro]
230pub fn template(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
231    if let Lit::Str(s) = parse_macro_input!(src as ExprLit).lit {
232        match iovalue_from_str(&s.value()) {
233            Ok(v) => {
234                let e = ValueCompiler::for_templates().compile(&v);
235                // println!("{:#}", &e);
236                return e.into();
237            }
238            Err(_) => (),
239        }
240    }
241
242    panic!("Expected literal string containing the template and no more");
243}
244
245//---------------------------------------------------------------------------
246
247#[proc_macro]
248pub fn pattern(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
249    pat::pattern(src)
250}
251
252//---------------------------------------------------------------------------
253
254#[proc_macro]
255pub fn during(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
256    dur::during(src)
257}
258
259#[proc_macro]
260pub fn on_message(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
261    dur::on_message(src)
262}