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