1extern crate proc_macro;
4
5use std::mem;
6use std::collections::{HashSet, HashMap};
7use std::hash::Hash;
8use std::sync::{Mutex, Arc};
9use std::time::SystemTime;
10use syn::{self, DeriveInput, Data, Attribute, Meta, NestedMeta, Lit, Ident, ItemFn, ReturnType, Type, TypePath, Path, PathSegment, PathArguments, FnArg, PatType, Pat};
11use crate::proc_macro::TokenStream;
12use proc_macro2::Span;
13use quote::quote;
14use once_cell::sync::OnceCell;
15use lemon_mint::LemonMintBuilder;
16use std::borrow::Cow;
17use std::fmt::Write;
18
19static DERIVE: OnceCell<Derive> = OnceCell::new();
20
21#[proc_macro_derive(LemonTree, attributes(lem, lem_opt))]
23pub fn derive_lemon_tree(input: TokenStream) -> TokenStream
24{ match DERIVE.get_or_init(|| Default::default()).derive(input, true)
25 { Ok(ts) => ts,
26 Err(error) => panic!("{}", error),
27 }
28}
29
30#[proc_macro_derive(LemonTreeNode, attributes(lem))]
32pub fn derive_lemon_tree_node(input: TokenStream) -> TokenStream
33{ match DERIVE.get_or_init(|| Default::default()).derive(input, false)
34 { Ok(ts) => ts,
35 Err(error) => panic!("{}", error),
36 }
37}
38
39#[proc_macro_attribute]
41pub fn lem_fn(attr: TokenStream, item: TokenStream) -> TokenStream
42{ match DERIVE.get_or_init(|| Default::default()).lem_fn_attr(attr, item)
43 { Ok(item) => item,
44 Err(error) => panic!("{}", error),
45 }
46}
47
48struct Alts
49{ alts: Vec<String>,
50 split_points: Vec<usize>,
51}
52impl Alts
53{ pub fn split(mut value: &str, output: &mut Vec<String>) -> Result<(), String>
54 { let mut this = Self
55 { alts: vec![String::with_capacity(value.len())],
56 split_points: vec![1],
57 };
58 value = value.trim();
59 while value.len() != 0
60 { let pos = value.bytes().position(|c| c==b'[' || c==b']').unwrap_or(value.len());
61 this.push_str(&value[.. pos]);
62 if pos == value.len()
63 { break;
64 }
65 if value.as_bytes()[pos] == b'['
66 { this.opt_start();
67 }
68 else
69 { this.opt_end()?;
70 }
71 value = &value[pos+1 ..];
72 }
73 if this.split_points.len() != 1
74 { return Err(format!("Square bracket not closed"));
75 }
76 output.extend_from_slice(&this.alts);
77 Ok(())
78 }
79
80 fn push_str(&mut self, s: &str)
81 { let active = self.split_points[0];
82 let from = self.alts.len() - active;
83 for a in &mut self.alts[from ..]
84 { a.push_str(s);
85 }
86 }
87
88 fn opt_start(&mut self)
89 { let active = self.split_points[0];
90 self.alts.reserve(active);
91 for i in self.alts.len()-active .. self.alts.len()
92 { self.alts.push(self.alts[i].clone());
93 }
94 self.split_points.push(self.alts.len());
95 }
96
97 fn opt_end(&mut self) -> Result<(), String>
98 { if self.split_points.len() <= 1
99 { return Err(format!("Unbalanced square brackets"));
100 }
101 self.split_points.remove(0);
102 Ok(())
103 }
104}
105
106enum CurBuilder
107{ Active(LemonMintBuilder, String), Complete(usize), }
110
111#[derive(Clone, Hash, PartialEq, Eq)]
112struct Ns
113{ name: Arc<String>,
114 is_real_file: bool,
115}
116
117#[derive(Default)]
118struct StringsPool
119{ pool: HashSet<Arc<String>>,
120}
121impl StringsPool
122{ pub fn get(&mut self, s: String) -> Arc<String>
123 { let s = Arc::new(s);
124 match self.pool.get(&s)
125 { None => s,
126 Some(s) => Arc::clone(&s),
127 }
128 }
129
130 pub fn clear(&mut self)
131 { self.pool.clear();
132 }
133}
134
135#[derive(Default)]
136struct Derive
137{ builders: Mutex<HashMap<Ns, CurBuilder>>,
138 strings_pool: Mutex<StringsPool>,
139 last_use: Mutex<HashMap<Ns, (SystemTime, usize)>>,
140}
141
142impl Derive
143{ pub fn derive(&self, input: TokenStream, is_start_symbol: bool) -> Result<TokenStream, String>
144 { let (ns, filename, n_line) = self.get_location(input.clone())?;
145 self.reinit_if_needed(&ns, n_line);
146 let ast: &DeriveInput = &syn::parse(input).unwrap();
147 let name = &ast.ident;
148 self.add_type(&ns, &filename, n_line, name.to_string(), false)?;
149 self.parse_lem_opt_attrs(&ns, &filename, n_line, &ast.attrs)?;
150 match &ast.data
151 { Data::Enum(data_enum) =>
152 { for variant in &data_enum.variants
153 { let variant_name = &variant.ident;
154 for value in Self::parse_lem_attrs(&variant.attrs).map_err(|e| format!("In enum variant {}: {}", variant.ident, e))?
155 { if !value.trim().is_empty()
156 { let (value, aliases) = Self::parse_rhs(&value, variant.fields.len(), None, false).map_err(|e| format!("In enum variant {}: {}", variant.ident, e))?;
157 let mut action = quote!();
158 for i in 0 .. variant.fields.len()
159 { if aliases.iter().position(|&a| a==i).is_some()
160 { let arg = Ident::new(&format!("arg_{}", i), Span::call_site());
161 action = quote!(#action #arg.into(),);
162 }
163 else
164 { action = quote!(#action std::default::Default::default(),);
165 }
166 }
167 if variant.fields.len() > 0
168 { action = quote!((#action));
169 }
170 self.add_rule(&ns, &filename, n_line, name.to_string(), value, quote!(super::super::#name::#variant_name #action).to_string()).map_err(|e| format!("In enum variant {}: {}", variant.ident, e))?;
171 }
172 }
173 }
174 }
175 Data::Struct(data_struct) =>
176 { for value in Self::parse_lem_attrs(&ast.attrs)?
177 { if !value.trim().is_empty()
178 { let mut field_names = Vec::with_capacity(data_struct.fields.len());
179 let mut field_names_str = Vec::with_capacity(data_struct.fields.len());
180 for field in data_struct.fields.iter()
181 { if let Some(ref field_name) = field.ident
182 { field_names_str.push(field_name.to_string());
183 field_names.push(field_name);
184 }
185 }
186 let (value, aliases) = Self::parse_rhs(&value, field_names_str.len(), Some(&field_names_str), false)?;
187 let mut action = quote!();
188 let mut comma = false;
189 for (i, field_name) in field_names.into_iter().enumerate()
190 { if aliases.iter().position(|&n| i==n).is_some()
191 { if comma
192 { action = quote!(#action,);
193 }
194 comma = true;
195 action = quote!(#action #field_name: #field_name.into());
196 }
197 else
198 { if comma
199 { action = quote!(#action,);
200 }
201 comma = true;
202 action = quote!(#action #field_name: std::default::Default::default());
203 }
204 }
205 action = quote!(super::super::#name {#action});
206 self.add_rule(&ns, &filename, n_line, name.to_string(), value, action.to_string())?;
207 }
208 }
209 },
210 Data::Union(_data_union) =>
211 { return Err("Not implemented".to_string());
212 },
213 }
214 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
215 let mut code = quote!
216 { impl #impl_generics lemon_tree::LemonTreeNode for #name #ty_generics #where_clause
217 {
218 }
219 };
220 if is_start_symbol
221 { let rust = self.get_lemon_result(&ns, &filename, n_line, name.to_string())?;
222 let rust: proc_macro2::TokenStream = rust.parse().unwrap();
223 let ns_name = Ident::new(ns.name.as_ref(), Span::call_site());
224 code = quote!
225 { #code
226 impl #impl_generics lemon_tree::LemonTree for #name #ty_generics #where_clause
227 { type Token = #ns_name::code::Token;
228 type Parser = #ns_name::code::Parser;
229 }
230 impl #impl_generics #name #ty_generics #where_clause
231 { pub fn get_parser(extra: #ns_name::code::ExtraArgumentType) -> #ns_name::code::Parser
232 { #ns_name::code::Parser::new(extra)
233 }
234 }
235 };
236 if !cfg!(feature = "debug-parser-to-file")
237 { code = quote![#code mod #ns_name { #rust }];
238 }
239 else
240 { code = quote![#code mod #ns_name;];
241 use std::io::{Read, Write, Seek, SeekFrom};
242 use std::fs::{OpenOptions, create_dir};
243 use std::path::PathBuf;
244 let rust_str = rust.to_string();
245 let mut path = PathBuf::from(filename.as_ref());
246 path.pop();
247 path.push(ns.name.as_ref());
248 if !path.exists()
249 { create_dir(&path).unwrap();
250 }
251 path.push(format!("{}.rs", ns.name));
252 let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).map_err(|e| e.to_string())?;
253 let mut cur_rust_str = Vec::new();
254 file.read_to_end(&mut cur_rust_str).unwrap();
255 if cur_rust_str != rust_str.as_bytes()
256 { file.seek(SeekFrom::Start(0)).unwrap();
257 file.set_len(0).unwrap();
258 write!(file, "{} ", rust_str).unwrap();
259 }
260 }
261 }
262 self.log_last_use(&ns, n_line);
263 Ok(code.into())
264 }
265
266 fn log_last_use(&self, ns: &Ns, n_line: usize)
267 { self.last_use.lock().unwrap().insert(ns.clone(), (SystemTime::now(), n_line));
268 }
269
270 fn reinit_if_needed(&self, ns: &Ns, n_line: usize)
274 { let mut want_reinit = false;
275 if let Some((at, at_n_line)) = self.last_use.lock().unwrap().get_mut(ns)
276 { if n_line <= *at_n_line
277 { if let Ok(elapsed) = SystemTime::now().duration_since(*at)
278 { if elapsed.as_millis() > 1000
279 { want_reinit = true;
280 }
281 else
282 { let map = self.builders.lock().unwrap();
284 if let Some(CurBuilder::Complete(_)) = map.get(ns)
285 { want_reinit = true;
286 }
287 }
288 }
289 }
290 }
291 if want_reinit
292 { self.builders.lock().unwrap().clear();
293 self.strings_pool.lock().unwrap().clear();
294 self.last_use.lock().unwrap().clear();
295 }
296 }
297
298 fn parse_lem_attrs(attrs: &Vec<Attribute>) -> Result<Vec<String>, String>
299 { let mut values = Vec::new();
300 for a in attrs
301 { match a.parse_meta()
302 { Ok(Meta::List(list)) =>
303 { if list.path.is_ident("lem")
304 { for a in list.nested.iter()
305 { match a
306 { NestedMeta::Lit(Lit::Str(s)) =>
307 { Alts::split(&s.value(), &mut values)?;
308 }
309 _ =>
310 { return Err(format!("Cannot parse #[lem(...)]: {}", quote!(#a).to_string()));
311 }
312 }
313 }
314 }
315 },
316 _ => {}
317 }
318 }
319 Ok(values)
320 }
321
322 fn parse_lem_opt_attrs(&self, ns: &Ns, filename: &Arc<String>, n_line: usize, attrs: &Vec<Attribute>) -> Result<(), String>
323 { for a in attrs
324 { match a.parse_meta()
325 { Ok(Meta::List(list)) =>
326 { if list.path.is_ident("lem_opt")
327 { for a in list.nested
328 { match a
329 { NestedMeta::Meta(Meta::NameValue(list)) =>
330 { self.with_builder
331 ( ns, filename, n_line, |builder, grammar|
332 { let value = match list.lit
333 { Lit::Str(s) => s.value(),
334 _ =>
335 { return Err("Option value in #[lem_opt(...)] must be string".to_string());
336 }
337 };
338 if list.path.is_ident("extra_argument")
339 { if cfg!(feature = "dump-grammar")
340 { writeln!(grammar, "%extra_argument {{super::super::{}}}", value).map_err(|_| format!("Write to string failed"))?;
341 }
342 builder.set_extra_argument_type(format!("super::super::{}", value)).map_err(|e| e.to_string())
343 }
344 else if list.path.is_ident("token_type")
345 { if cfg!(feature = "dump-grammar")
346 { writeln!(grammar, "%token_type {{{}}}", value).map_err(|_| format!("Write to string failed"))?;
347 }
348 else if cfg!(feature = "dump-lemon-grammar")
349 { writeln!(grammar, "%token_type {{int}}").map_err(|_| format!("Write to string failed"))?;
350 }
351 builder.set_token_type(value).map_err(|e| e.to_string())
352 }
353 else if list.path.is_ident("fallback")
354 { if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
355 { writeln!(grammar, "%fallback {}.", value.trim()).map_err(|_| format!("Write to string failed"))?;
356 }
357 let v = value.trim_start();
358 let pos = v.find(|c: char| c.is_ascii_whitespace()).unwrap_or(v.len());
359 let fb_token = &v[.. pos];
360 let v = &v[pos ..];
361 builder.add_fallback(filename, n_line, fb_token.to_string(), v).map_err(|e| e.to_string())
362 }
363 else if list.path.is_ident("left")
364 { if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
365 { writeln!(grammar, "%left {}.", value.trim()).map_err(|_| format!("Write to string failed"))?;
366 }
367 builder.set_left(filename, n_line, &value).map_err(|e| e.to_string())
368 }
369 else if list.path.is_ident("right")
370 { if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
371 { writeln!(grammar, "%right {}.", value.trim()).map_err(|_| format!("Write to string failed"))?;
372 }
373 builder.set_right(filename, n_line, &value).map_err(|e| e.to_string())
374 }
375 else if list.path.is_ident("nonassoc")
376 { if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
377 { writeln!(grammar, "%nonassoc {}.", value.trim()).map_err(|_| format!("Write to string failed"))?;
378 }
379 builder.set_nonassoc(filename, n_line, &value).map_err(|e| e.to_string())
380 }
381 else if list.path.is_ident("trace")
382 { if cfg!(feature = "dump-grammar")
383 { writeln!(grammar, "%trace {{{}}}", value).map_err(|_| format!("Write to string failed"))?;
384 }
385 builder.set_trace_prompt(value).map_err(|e| e.to_string())
386 }
387 else
388 { Err("Unknown option in #[lem_opt(...)]. Valid options are: token_type=\"Name\", fallback=\"FB A B C\", left=\"A B C\", right=\"A B C\", nonassoc=\"A B C\", trace=\"prompt\"".to_string())
389 }
390 }
391 )?;
392 }
393 _ =>
394 { return Err("Cannot parse #[lem_opt(...)]".to_string());
395 }
396 }
397 }
398 }
399 },
400 _ => {}
401 }
402 }
403 Ok(())
404 }
405
406 fn parse_rhs(rhs: &str, n_fields: usize, field_names: Option<&Vec<String>>, is_fn: bool) -> Result<(String, Vec<usize>), String>
410 { let mut subst = String::with_capacity(rhs.len()+128);
411 let mut aliases = Vec::new();
412 let mut s = rhs.trim_start();
413 while s.len() != 0
414 { let name_len = s.chars().position(|c| !c.is_ascii_alphanumeric() && c!='_').unwrap_or(s.len());
415 if name_len == 0
416 { return Err(format!("Invalid RHS expression: \"{}\"", rhs));
417 }
418 subst.push_str(&s[.. name_len]);
419 s = s[name_len ..].trim_start();
420 if s.bytes().next() == Some(b'(')
421 { s = s[1 ..].trim_start();
422 let alias_len = s.chars().position(|c| !c.is_ascii_alphanumeric() && c!='_').unwrap_or(s.len());
423 let alias = &s[.. alias_len];
424 let n = field_names
425 .and_then(|v| v.iter().position(|a| a==alias)) .or_else(|| alias.parse().ok()) .filter(|&n| n < n_fields) .ok_or_else
429 ( ||
430 match (field_names, is_fn)
431 { (Some(field_names), true) => format!("No such function argument: {}. Options: {}, or number from 0 to {} exclusive", alias, field_names.join(", "), n_fields),
432 (Some(field_names), false) => format!("No such struct field: {}. Options: {}", alias, field_names.join(", ")),
433 (None, _) => format!("No such field number in enum variant: {}. Options: 0 .. {} (exclusive)", alias, n_fields),
434 }
435 )?;
436 aliases.push(n);
437 subst.push('(');
438 if let Some(field_names) = field_names
439 { subst.push_str(&field_names[n]);
440 }
441 else
442 { subst.push_str("arg_");
443 subst.push_str(alias);
444 }
445 subst.push(')');
446 s = s[alias_len ..].trim_start();
447 if alias_len==0 || s.bytes().next() != Some(b')')
448 { return Err(format!("Invalid alias in RHS expression: \"{}\"", rhs));
449 }
450 s = s[1 ..].trim_start();
451 }
452 subst.push(' ');
453 }
454 Ok((subst, aliases))
455 }
456
457 fn get_location(&self, input: TokenStream) -> Result<(Ns, Arc<String>, usize), String>
458 { let span = input.into_iter().next().unwrap().span();
459 let filename_str = span.file();
460 let n_line = span.line();
461 let path = std::path::Path::new(&filename_str);
462 let is_real_file = path.exists();
463 let mut strings_pool = self.strings_pool.lock().unwrap();
464 let stem = path.file_stem().map(|s| s.to_string_lossy().into_owned()).unwrap_or_else(|| filename_str.clone());
465 let ns_name = strings_pool.get(stem);
466 let filename = strings_pool.get(filename_str);
467 Ok((Ns{name: ns_name, is_real_file}, filename, n_line))
468 }
469
470 fn with_builder<F>(&self, ns: &Ns, filename: &Arc<String>, n_line: usize, cb: F) -> Result<(), String> where F: FnOnce(LemonMintBuilder, &mut String) -> Result<LemonMintBuilder, String>
471 { let mut map = self.builders.lock().unwrap();
472 match map.get_mut(ns)
473 { None =>
474 { let mut builder = LemonMintBuilder::new();
475 let mut grammar = String::new();
476 builder = cb(builder, &mut grammar)?;
477 map.insert(ns.clone(), CurBuilder::Active(builder, grammar));
478 Ok(())
479 }
480 Some(cur_builder) =>
481 { let my_cur_builder = mem::replace(cur_builder, CurBuilder::Complete(usize::MAX));
482 match my_cur_builder
483 { CurBuilder::Active(mut builder, mut grammar) =>
484 { builder = cb(builder, &mut grammar)?;
485 *cur_builder = CurBuilder::Active(builder, grammar);
486 Ok(())
487 }
488 CurBuilder::Complete(complete_n_line) =>
489 { *cur_builder = CurBuilder::Complete(complete_n_line);
490 Err(format!("#[derive(LemonTree)] must be the last in file. Found at line {}, but additional directive at line {} in file {}", complete_n_line, n_line, filename))
491 }
492 }
493 }
494 }
495 }
496
497 fn add_type(&self, ns: &Ns, filename: &Arc<String>, n_line: usize, name: String, if_not_exists: bool) -> Result<(), String>
498 { self.with_builder
499 ( ns, filename, n_line, move |builder, grammar|
500 { let rust_type = format!("super::super::{}", name);
501 if cfg!(feature = "dump-grammar")
502 { writeln!(grammar, "%type {} {{{}}}", name, rust_type).map_err(|_| format!("Write to string failed"))?;
503 }
504 if !if_not_exists || builder.get_type(&name).is_none()
505 { builder.add_type(filename, n_line, name, rust_type).map_err(|e| e.to_string())
506 }
507 else
508 { Ok(builder)
509 }
510 }
511 )
512 }
513
514 fn add_rule(&self, ns: &Ns, filename: &Arc<String>, n_line: usize, lhs_name: String, rhs: String, code: String) -> Result<(), String>
515 { self.with_builder
516 ( ns, filename, n_line, move |builder, grammar|
517 { if cfg!(feature = "dump-grammar")
518 { writeln!(grammar, "{} ::= {}. {{{}}}", lhs_name, rhs.trim(), code).map_err(|_| format!("Write to string failed"))?;
519 }
520 else if cfg!(feature = "dump-lemon-grammar")
521 { writeln!(grammar, "{} ::= {}.", Self::lc_first(&lhs_name), Self::rhs_to_lemon(rhs.trim())).map_err(|_| format!("Write to string failed"))?;
522 }
523 builder.add_rule(filename, n_line, lhs_name, &rhs, code).map_err(|e| e.to_string())
524 }
525 )
526 }
527
528 fn get_lemon_result(&self, ns: &Ns, filename: &Arc<String>, n_line: usize, start_symbol_name: String) -> Result<String, String>
529 { let mut map = self.builders.lock().unwrap();
530 match map.remove(ns)
531 { None =>
532 { Err("No parser rules".to_string())
533 }
534 Some(CurBuilder::Active(mut builder, grammar)) =>
535 { if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
536 { eprintln!("/* Parser {} from {} */", ns.name, filename);
537 }
538 if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
539 { eprintln!("%start_symbol {{{}}}", if cfg!(feature = "dump-lemon-grammar") {Self::lc_first(&start_symbol_name).into()} else {Cow::from(&start_symbol_name)});
540 }
541 builder = builder.set_start_symbol(&filename, n_line, start_symbol_name).map_err(|e| e.to_string())?;
542 if cfg!(feature = "dump-grammar") || cfg!(feature = "dump-lemon-grammar")
543 { eprintln!("{}", grammar);
544 }
545 let mut rust = Vec::new();
546 let lemon = builder.try_into_lemon().map_err(|e| e.to_string())?;
547 lemon.gen_rust(&mut rust).map_err(|e| e.to_string())?;
548 map.insert
549 ( ns.clone(),
550 if ns.is_real_file
551 { CurBuilder::Complete(n_line)
552 }
553 else { CurBuilder::Active(LemonMintBuilder::new(), String::new())
555 }
556 );
557 String::from_utf8(rust).map_err(|e| e.to_string())
558 }
559 Some(CurBuilder::Complete(complete_n_line)) =>
560 { Err(format!("Double #[derive(LemonTree)] in the same file. First at line {}, and then at line {} in file {}", complete_n_line, n_line, filename))
561 }
562 }
563 }
564
565 fn lem_fn_attr(&self, args: TokenStream, item: TokenStream) -> Result<TokenStream, String>
566 { let (ns, filename, n_line) = self.get_location(item.clone())?;
567 self.reinit_if_needed(&ns, n_line);
568 let values = Self::lem_fn_attr_parse(args)?;
569 let (fn_name, mut fn_args, fn_return) = Self::lem_fn_attr_parse_fn(item.clone())?;
570 let mut has_extra = false;
571 for (i, arg) in fn_args.iter().enumerate()
572 { if arg == "extra"
573 { if i != fn_args.len()-1
574 { return Err(format!("Extra argument must be last"));
575 }
576 has_extra = true;
577 }
578 }
579 if has_extra
580 { fn_args.pop();
581 }
582 self.add_type(&ns, &filename, n_line, fn_return.clone(), true)?;
583 for value in values
584 { if !value.trim().is_empty()
585 { let (value, aliases) = Self::parse_rhs(&value, fn_args.len(), Some(&fn_args), true)?;
586 let mut action = quote!();
587 for i in 0 .. fn_args.len()
588 { if i > 0
589 { action = quote!(#action,);
590 }
591 if aliases.iter().position(|&a| a==i).is_some()
592 { let arg = Ident::new(&fn_args[i], Span::call_site());
593 action = quote!(#action #arg.into());
594 }
595 else
596 { action = quote!(#action std::default::Default::default());
597 }
598 }
599 if has_extra
600 { if fn_args.len() > 0
601 { action = quote!(#action,);
602 }
603 action = quote!(#action extra);
604 }
605 self.add_rule(&ns, &filename, n_line, fn_return.clone(), value, quote!(super::super::#fn_name(#action)).to_string())?;
606 }
607 }
608 self.log_last_use(&ns, n_line);
609 Ok(item)
610 }
611
612 fn lem_fn_attr_parse(args: TokenStream) -> Result<Vec<String>, String>
613 { let derive_input = format!("#[lemon({})] struct Tmp;", args);
614 let ast: &DeriveInput = &syn::parse_str(&derive_input).map_err(|_| "Cannot parse #[lemon(...)]".to_string())?;
615 let attr = ast.attrs.first().ok_or_else(|| "Cannot parse #[lemon(...)]".to_string())?;
616 match attr.parse_meta()
617 { Ok(Meta::List(list)) =>
618 { if list.path.is_ident("lemon")
619 { for a in list.nested.iter()
620 { match a
621 { NestedMeta::Lit(Lit::Str(s)) =>
622 { let mut values = Vec::with_capacity(1);
623 Alts::split(&s.value(), &mut values)?;
624 return Ok(values);
625 }
626 _ =>
627 { return Err("Cannot parse #[lemon(...)]".to_string());
628 }
629 }
630 }
631 }
632 },
633 _ => {}
634 }
635 Err("Cannot parse #[lemon(...)]".to_string())
636 }
637
638 fn lem_fn_attr_parse_fn(item: TokenStream) -> Result<(Ident, Vec<String>, String), String>
639 { let ast: &ItemFn = &syn::parse(item).map_err(|e| e.to_string())?;
640 let fn_name = ast.sig.ident.clone();
642 let mut fn_return = None;
644 let output = &ast.sig.output;
645 match output
646 { ReturnType::Default =>
647 { return Err(format!("Function must return an object"));
648 }
649 ReturnType::Type(_, ty) =>
650 { match &**ty
651 { Type::Path(TypePath {qself: None, path: Path{segments, ..}}) =>
652 { match segments.last()
653 { Some(PathSegment {ident, arguments: PathArguments::None}) =>
654 { fn_return = Some(ident);
655 }
656 _ => {}
657 }
658 }
659 _ => {}
660 }
661 }
662 };
663 let fn_return = fn_return.ok_or_else(|| format!("Couldn't understand function return type. It must be LHS symbol object. Found: {}", quote!(#output)))?;
664 let mut fn_args = Vec::new();
666 for arg in &ast.sig.inputs
667 { match arg
668 { FnArg::Receiver(_) =>
669 { return Err(format!("Must be global function"));
670 }
671 FnArg::Typed(PatType {pat, ..}) =>
672 { match &**pat
673 { Pat::Ident(pat_ident) =>
674 { let mut pat_ident = pat_ident.clone();
675 pat_ident.mutability = None;
676 fn_args.push(quote!(#pat_ident).to_string());
677 }
678 _ => {}
679 }
680 }
681 }
682 }
683 Ok((fn_name, fn_args, fn_return.to_string()))
684 }
685
686 fn lc_first(s: &str) -> String
687 { let mut chars = s.chars();
688 chars
689 .next()
690 .map(|first_letter| first_letter.to_lowercase())
691 .into_iter()
692 .flatten()
693 .chain(chars)
694 .collect()
695 }
696
697 fn rhs_to_lemon(rhs: &str) -> String
699 { let mut result = String::new();
700 let mut rhs = rhs.trim();
701 while !rhs.is_empty()
702 { let pos = rhs.find(|c: char| c.is_ascii_alphanumeric() || c=='_' || c=='(').unwrap_or(rhs.len());
703 write!(result, "{}", &rhs[.. pos]).unwrap();
704 rhs = &rhs[pos ..];
705 if rhs.as_bytes()[0] == b'('
706 { let pos = rhs.find(|c: char| c==')').unwrap_or(rhs.len()-1) + 1;
708 rhs = &rhs[pos ..];
709 }
710 else
711 { let pos = rhs.find(|c: char| !c.is_ascii_alphanumeric() && c!='_').unwrap_or(rhs.len());
712 let name = &rhs[.. pos];
713 if name.find(|c: char| c.is_ascii_lowercase()).is_some()
714 { result.reserve(name.len());
716 let mut name = name.chars();
717 write!(result, "{}", name.next().unwrap().to_lowercase()).unwrap(); for c in name
719 { result.push(c);
720 }
721 }
722 else
723 { write!(result, "{}", name).unwrap();
724 }
725 rhs = &rhs[pos ..];
726 }
727 }
728 result
729 }
730}