lemon_tree_derive/
lib.rs

1//! Helper crate for [lemon-tree](https://crates.io/crates/lemon-tree). Rust-way parser builder.
2
3#![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/// Makes enum/struct a start symbol of current parser. Must appear the last parser attribute in file.
27#[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/// Makes enum/struct a regular nonterminal symbol. Must appear before `#[derive(LemonTree)]` and `#[lem_fn()]` in the same rust file.
36#[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/// Makes module-global public function an action for specified Lemon parser expression.
45#[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), // (builder, grammar)
113	Complete(usize), // line where start symbol occured
114}
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	/// This proc-macro crate can be reused by compilers and development tools.
276	/// The same instance can be used to scan the same file several times.
277	/// If i see that n_line is <= than the last call n_line, and either more than 1 sec passed since last call, or current builder complete, then i will reinitialize self.
278	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					{	// maybe requested parser complete
288						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	// enum: parse_rhs(rhs, n_fields, None, false)
412	// struct: parse_rhs(rhs, n_fields, Some(field_names), false)
413	// fn: parse_rhs(rhs, n_fields, Some(field_names), true)
414	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)) // there are field_names and find alias in field_names
431					.or_else(|| alias.parse().ok()) // or parse alias
432					.filter(|&n| n < n_fields) // accept only 0 .. n_fields
433					.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 // allow multiple builders in the same file, if this is not a real file (like doc comment)
558					{	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		// fn_name
645		let fn_name = ast.sig.ident.clone();
646		// fn_return
647		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		// fn_args
669		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	/// Remove aliases, and convert each nonterminal symbol in RHS expression. For me any name that contains lowercase letter is nonterminal, but for lemon first letter must be lowercased.
702	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			{	// skip aliases
711				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				{	// is nonterminal
719					result.reserve(name.len());
720					let mut name = name.chars();
721					write!(result, "{}", name.next().unwrap().to_lowercase()).unwrap(); // we know that string is not empty, because there is at least 1 lowercased letter
722					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}