Skip to main content

lemon_tree_derive/
lib.rs

1//! Helper crate for [lemon-tree](https://crates.io/crates/lemon-tree). Rust-way parser builder.
2
3extern 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/// Makes enum/struct a start symbol of current parser. Must appear the last parser attribute in file.
22#[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/// Makes enum/struct a regular nonterminal symbol. Must appear before `#[derive(LemonTree)]` and `#[lem_fn()]` in the same rust file.
31#[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/// Makes module-global public function an action for specified Lemon parser expression.
40#[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), // (builder, grammar)
108	Complete(usize), // line where start symbol occured
109}
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	/// This proc-macro crate can be reused by compilers and development tools.
271	/// The same instance can be used to scan the same file several times.
272	/// 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.
273	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					{	// maybe requested parser complete
283						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	// enum: parse_rhs(rhs, n_fields, None, false)
407	// struct: parse_rhs(rhs, n_fields, Some(field_names), false)
408	// fn: parse_rhs(rhs, n_fields, Some(field_names), true)
409	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)) // there are field_names and find alias in field_names
426					.or_else(|| alias.parse().ok()) // or parse alias
427					.filter(|&n| n < n_fields) // accept only 0 .. n_fields
428					.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 // allow multiple builders in the same file, if this is not a real file (like doc comment)
554					{	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		// fn_name
641		let fn_name = ast.sig.ident.clone();
642		// fn_return
643		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		// fn_args
665		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	/// 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.
698	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			{	// skip aliases
707				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				{	// is nonterminal
715					result.reserve(name.len());
716					let mut name = name.chars();
717					write!(result, "{}", name.next().unwrap().to_lowercase()).unwrap(); // we know that string is not empty, because there is at least 1 lowercased letter
718					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}