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)] 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 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 return e.into();
237 }
238 Err(_) => (),
239 }
240 }
241
242 panic!("Expected literal string containing the template and no more");
243}
244
245#[proc_macro]
248pub fn pattern(src: proc_macro::TokenStream) -> proc_macro::TokenStream {
249 pat::pattern(src)
250}
251
252#[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}