rustlr/
ast_writer.rs

1#![allow(dead_code)]
2#![allow(unused_variables)]
3#![allow(non_snake_case)]
4#![allow(non_camel_case_types)]
5#![allow(unused_parens)]
6#![allow(unused_mut)]
7#![allow(unused_assignments)]
8#![allow(unused_doc_comments)]
9#![allow(unused_imports)]
10use std::collections::{HashMap,HashSet};
11//use std::cell::{RefCell,Ref,RefMut};
12use std::io::{self,Read,Write,BufReader,BufRead};
13use std::fs::File;
14use std::io::prelude::*;
15use std::path::Path;
16use crate::{Grammar,is_alphanum,emptybox,checkboxexp};
17
18// metaast for asts
19// grammar_processor needs to keep set of nti's to have types flattened.
20// keep a hashmap from nt names to structs
21// structasts:HashMap<usize,(simpletypebool,Vec<(labelString,typename)>)>
22// generate all struct types first and store in table,
23// then generate enums.   complements toextend.
24// How can structs flatten into structs?  By changing the definition
25// into structasts.  How to prevent circular flattening? make sure flatten
26// target is not reachable from itself using the reachability_type relation
27// Howabout need for lbox because of reachability? more lboxes
28// ok, not less....
29
30// first bool is simpletype, second bool is flatten-able, i32 is passthru
31// String is type rep, fields are rhs index, label, alreadylbox, type
32type METAASTTYPE = HashMap<usize,(bool,bool,i32,String,Vec<(usize,String,bool,String)>)>;
33
34// auto-generate abstract syntax
35// prepare Grammar - after parse_grammar first creates grammar
36impl Grammar
37{
38   fn prepare(&mut self) -> String
39   {
40     // reachability already called by grammar parser, call reachability_types:
41     // at this point, self.Reachable can be cloned if needs to be preserved
42     self.reachability_types();
43     
44     // assign types to all non-terminal symbols
45     // first pass: assign types to "" types, skip all others
46     let mut ntcx = self.ntcxmax+1;
47     for nt in self.Rulesfor.keys() { // for each nonterminal index
48//println!("TYPE FOR {}: {}",&self.Symbols[*nt].sym,&self.Symbols[*nt].rusttype);       
49       if self.Symbols[*nt].rusttype.len()==0 { // type "" means generate type
50         // determine if lifetime needed.
51         let reach = self.Reachable.get(nt).unwrap();
52/////
53//for r in reach.iter() {println!("{} reaches {}",&self.Symbols[*nt].sym,&self.Symbols[*r].sym);}
54/////
55         let mut needlt = false;
56         if self.lifetime.len()>0 {
57           for ti in self.haslt_base.iter() {
58             if reach.contains(ti) {needlt = true; break;}
59           }
60         }//if lifetime check needed
61         if needlt {
62           self.Symbols[*nt].rusttype = format!("{}<{}>",&self.Symbols[*nt].sym,&self.lifetime);
63         } else {
64           self.Symbols[*nt].rusttype = self.Symbols[*nt].sym.clone();
65         }//don't need lt
66         self.enumhash.insert(self.Symbols[*nt].rusttype.clone(),ntcx);
67         ntcx+=1;
68       }//need type assignment during first pass
69     }// first pass
70
71     // Set of nti that will extend other types
72     let mut toextend = HashMap::new();  // usize->usize nti's
73     let mut extendtargets = HashSet::new();
74     
75     //// second pass: change @EXPR to actual type, change :Expr to direct
76     for nt in self.Rulesfor.keys() {
77       // two possibilities : @expr, or <@expr> or :Expr
78       // assume only one.
79       let addtoextend = self.Symbols[*nt].rusttype.starts_with(':');
80       let mut addtosymhash = false; // because already added above
81       let mut limit = self.Symbols.len()+1;
82       let mut indirect = true;
83       while (indirect || self.Symbols[*nt].rusttype.contains('@')) && limit>0
84       {
85         indirect = false;
86         addtosymhash = true;
87         let stype = &self.Symbols[*nt].rusttype; //reborrow
88         let mut symtocopy = ""; // symbol to copy type from
89         let (mut start,mut end) = (0,stype.len());
90         if stype.starts_with(':') || stype.starts_with('@') {
91           symtocopy = stype[1..].trim();
92         } else if let Some(pos1)=stype.find("<@") {
93           if let Some(pos2)=stype[pos1+2..].find('>') {
94              symtocopy = &stype[pos1+2..pos1+2+pos2];
95              start = pos1+1; end = pos1+2+pos2;
96           }
97         } else if let Some(pos1)=stype.find("<:") {
98           if let Some(pos2)=stype[pos1+2..].find('>') {
99              symtocopy = stype[pos1+2..pos1+2+pos2].trim();
100              start = pos1+1; end = pos1+2+pos2;
101              indirect = true; // make sure              
102           }
103         }         
104         if symtocopy.len()>0 {
105           let symi = *self.Symhash.get(symtocopy).unwrap();
106           let mut replacetype = self.Symbols[symi].rusttype.clone();
107           if replacetype.starts_with(':') {indirect = true;}
108           else if addtoextend {
109              toextend.insert(*nt,symi);
110//println!("{} will extend {}",&self.Symbols[*nt].sym,&self.Symbols[symi].sym);
111              extendtargets.insert(symi);
112           }
113
114           // change type to actual type.
115           
116           let mut newtype = stype.clone();
117           newtype.replace_range(start..end,&replacetype);
118           self.Symbols[*nt].rusttype = newtype;
119         } // if symtocopy.len>0
120         limit -= 1;
121       }//while still contains @ - keep doing it
122       if addtosymhash && limit>0 {self.enumhash.insert(self.Symbols[*nt].rusttype.clone(),ntcx); ntcx+=1;}
123       else if limit==0 {
124          let msg = format!("CIRCULARITY DETECTED IN PROCESSING TYPE DEPENDENCIES (type {} for nonterminal {}). THIS TYPE WILL BE RESET AND REGENERATED\n",&self.Symbols[*nt].rusttype,&self.Symbols[*nt].sym);
125          if self.tracelev>0 {eprint!("{}",msg);}
126          else { self.genlog.push_str(&msg); }
127          self.Symbols[*nt].rusttype = String::new();
128       }
129     }//second pass
130     
131     // final pass sets enumhash
132     self.ntcxmax = ntcx;
133     // grammar_processor also needs to set enumhash if not -auto
134
135     ////////////////////////////// struct generation stage
136     // third pass: generate structtypes first so they can be flattened,
137     // store generated types in metaast map:
138
139     // two mutually recursive types cannot flatten into each other
140     let mut flattentypes = self.flattentypes.clone();
141     for a in self.flattentypes.iter() {
142       let mut acanflatten = true;
143       if !flattentypes.contains(a) {continue;}
144       for b in self.flattentypes.iter() {
145         if a!=b && flattentypes.contains(b) {
146            let areach = self.Reachable.get(a).unwrap();
147            let breach = self.Reachable.get(b).unwrap();
148            if areach.contains(b) && breach.contains(a) {
149               flattentypes.remove(a); flattentypes.remove(b);
150               let msg = format!("WARNING: MUTUALLY RECURSIVE TYPES {} AND {} CANNOT FLATTEN INTO EACHOTHER\n",&self.Symbols[*a].sym,&self.Symbols[*b].sym);
151               if self.tracelev>0 {eprint!("{}",msg);}
152               else {self.genlog.push_str(&msg);}
153            }
154         }
155       }
156     }// discover mutually recursive flatten types
157
158     let mut structasts = METAASTTYPE::new(); 
159     for (nt,NTrules) in self.Rulesfor.iter() {  //first loop
160       if NTrules.len()!=1 || extendtargets.contains(nt) || toextend.contains_key(nt) { /*print warning*/ continue;}
161       let sri = *NTrules.iter().next().unwrap();
162       if self.Rules[sri].lhs.label.len()>0 {continue;}
163       let NT = &self.Symbols[*nt].sym;
164       let lhsymtype = self.Symbols[*nt].rusttype.clone();         
165       if !lhsymtype.starts_with(NT) {continue;}
166       let mut canflatten = true;
167       let mut simplestruct = true;
168       for rs in &self.Rules[sri].rhs {
169         if rs.label.len()>0 && !rs.label.starts_with("_item") && !emptybox(&rs.label)
170           { simplestruct = false; break; }
171       } //determine if simple struct
172       let ntsym = &self.Symbols[*nt];
173       let mut vfields = Vec::new(); // metaast vector representing fields
174       let mut rhsi = 0; // right-side index
175       let mut passthru:i32 = -1; // index of path-thru NT value
176       for rsym in self.Rules[sri].rhs.iter_mut() {
177         let expectedlabel = format!("_item{}_",&rhsi);
178         let alreadyislbxlab = rsym.label.len()>1 && rsym.label.starts_with('[') && rsym.label.ends_with(']');
179         let itemlabel = if rsym.label.len()>0 && &rsym.label!=&expectedlabel && !rsym.label.starts_with('@') {
180            // presence of rhs label also cancels passthru
181            passthru=-2; checkboxexp(&rsym.label,&expectedlabel).to_owned()
182            } else {expectedlabel};
183         if rsym.terminal && rsym.precedence!=0 { passthru = -2; }
184         let rsymtype = &self.Symbols[rsym.index].rusttype;
185         // check if rsym is non-terminal and reaches lsym
186         let lhsreachable = match self.Reachable.get(&rsym.index) {
187               None => false,
188               Some(rset) => rset.contains(nt),
189              };
190         let needrecursion = lhsreachable && !nonlbxtype(rsymtype);
191         if alreadyislbxlab || needrecursion {
192               if rsymtype==&lhsymtype && passthru==-1 {passthru=rhsi as i32;}
193               else {passthru = -2;}
194	       if !self.bumpast && needrecursion {
195                 vfields.push((rhsi,itemlabel.clone(),alreadyislbxlab,format!("LBox<{}>",rsymtype)));
196	       }
197	       else if alreadyislbxlab && !nonlctype(rsymtype) {
198 	         vfields.push((rhsi,itemlabel.clone(),alreadyislbxlab,format!("LC<{}>",rsymtype)));	       
199	       }
200         }// box label or need recursion
201         else if rsymtype!="()" || (rsym.label.len()>0 && !rsym.label.starts_with("_item")) {  //no Lbox, and not unit type without label
202           vfields.push((rhsi,itemlabel.clone(),alreadyislbxlab,rsymtype.to_owned()));
203           if rsymtype==&lhsymtype && passthru==-1 {passthru=rhsi as i32;}
204              else {passthru = -2;}
205         } //no Lbox, and not unit type without label
206         rhsi+=1;
207       } //for each symbol on right in a iter_mut()
208       structasts.insert(*nt,(simplestruct,canflatten,passthru,String::new(),vfields));
209     }// structs generation loop 1
210
211     // REAL struct generation loop: APPLY FLATTEN, create and set actions
212     // -only 1 levels of indirection allowed?
213     let mut newsa = HashMap::with_capacity(structasts.len());
214     for (nt,(simplestruct,canflatten,passthru,_,vecfields)) in structasts.iter() {
215       let sri = *self.Rulesfor.get(nt).unwrap().iter().next().unwrap();  // !
216       let NT = &self.Symbols[*nt].sym;
217       let lhsymtype = self.Symbols[*nt].rusttype.clone();
218       let ntsym = &self.Symbols[*nt];
219
220       let mut havedefault;
221       if let Some(defaultstruct) = self.defaults.get(nt) {
222         havedefault = format!("impl Default for {} {{ fn default()->Self {{ {} }} }}\n#[derive(Debug)]\n",&ntsym.rusttype, defaultstruct);
223       }
224       else {
225         havedefault = String::from("#[derive(Default,Debug)]\n");
226       }
227
228
229       let mut SAST = if !simplestruct {format!("{}pub struct {} {{\n",havedefault,&ntsym.rusttype)}
230         else {format!("{}pub struct {}(",havedefault,&ntsym.rusttype)};  // sets struct header
231       let mut fields = String::new();  // like "enumvar in previous version"
232       let mut vfields = Vec::new(); // (rhsi,label,type)
233
234       let mut SACTION = if *simplestruct {format!("{}(",NT)}
235               else {format!("{} {{",NT)};
236               
237       let mut viadjust:i32 = 0; //not used (not inc'ed)
238       for (rhsi,itemlabel,alreadylbx,rsymtype) in vecfields { //original field
239         let rhssymi = self.Rules[sri].rhs[*rhsi].index;
240         if rhssymi==*nt {
241            self.logeprint(&format!("WARNING: TYPE {} CANNOT FLATTEN INTO ITSELF\n",&self.Rules[sri].rhs[*rhsi].sym));
242         }
243         let mut flattened = false;
244         if rhssymi!=*nt && flattentypes.contains(&rhssymi) { // maybe able to flatten in
245           match structasts.get(&rhssymi) {
246             Some((simp,true,pthr,_,flatfields)) => {  //flatten in
247               if *pthr<0 && /* flatfields.len()>0 && */ (!simplestruct||*simp) && !self.Rules[sri].rhs[*rhsi].label.starts_with('[') {
248                 flattened=true;
249                 let mut fi = 0;
250                 for (frhsi,flab,albx,ftype) in flatfields {
251                   let newlab = format!("{}_{}",itemlabel,flab);
252                   let newactionlab = if *simp {format!("{}.{}",itemlabel,fi)}
253                       else {format!("{}.{}",itemlabel,flab)};
254                   let newindex = rhsi+(viadjust as usize)+fi;
255                   if *simplestruct {
256                     fields.push_str("pub ");
257                     fields.push_str(ftype); fields.push(',');
258                   } else {
259                     fields.push_str(&format!("  pub {}:{},\n",&newlab,ftype));
260                   }
261                   let islbxtype = ftype.starts_with("LBox<");
262                   if *simplestruct /*&& !islbxtype */{
263                     SACTION.push_str(&newactionlab); SACTION.push(',');
264                   }
265                   /*
266                   else if *simplestruct {
267                     SACTION.push_str(&format!("parser.lbx({},{}),",newindex,&newactionlab));
268                   }
269                   */
270                   else /* if !simplestruct  && (!islbxtype || *albx) */ {
271                    SACTION.push_str(&format!("{}:{}, ",&newlab,&newactionlab));
272                     
273                   }
274                   /*
275                   else { // !simplestruct and need to form parser.lbx
276                     SACTION.push_str(&format!("{}:parser.lbx({},{}), ",&newlab,newindex,&newactionlab));
277                   }
278                   */
279                   vfields.push((newindex,newlab,*albx,ftype.to_owned()));
280                   fi+=1;
281                 }//for each field in flatten source
282                 //viadjust += (flatfields.len() as i32)-1;
283               }//if can flatten
284             },
285             aaa => { 
286               //println!("NOT FLATTENING {}",&self.Symbols[rhssymi].sym);
287             }, //no flattening
288           }//match
289         }//if in flattentypes list
290         if !flattened {
291           let islbxtype = rsymtype.starts_with("LBox<");
292	   let islctype = rsymtype.starts_with("LC<");
293           if  *simplestruct {
294             fields.push_str("pub ");
295             fields.push_str(rsymtype); fields.push(',');
296             if islbxtype {
297               SACTION.push_str(&format!("parser.lbx({},{}),",rhsi+(viadjust as usize),itemlabel));
298             }
299	     else if islctype {
300               SACTION.push_str(&format!("parser.lc({},{}),",rhsi+(viadjust as usize),itemlabel));	     
301	     }
302	     else { SACTION.push_str(itemlabel); SACTION.push(','); }
303           } else { // not simplestruct
304             fields.push_str(&format!("  pub {}:{},\n",itemlabel,rsymtype));
305             /*
306             if (!islbxtype && !islctype) || *alreadylbx {
307               SACTION.push_str(&format!("{}:{}, ",itemlabel,itemlabel));
308             }
309	     else
310             */
311             if islctype {
312               SACTION.push_str(&format!("{}:parser.lc({},{}), ",itemlabel,rhsi+(viadjust as usize),itemlabel));	     
313	     }
314	     else if islbxtype {
315               SACTION.push_str(&format!("{}:parser.lbx({},{}), ",itemlabel,rhsi+(viadjust as usize),itemlabel));
316             }
317             else {
318               SACTION.push_str(&format!("{}:{}, ",itemlabel,itemlabel));
319             }
320           }//not simpletype
321           vfields.push((rhsi+(viadjust as usize),itemlabel.to_owned(),*alreadylbx,rsymtype.to_owned()));
322         }// !flatten
323       }//for each original field
324       // post actions
325       if *simplestruct {
326          fields.push_str(");\n\n");  SACTION.push(')');
327       } else {
328          fields.push_str("}\n\n");   SACTION.push('}');
329       }
330       SACTION.push_str(" }");
331       let mut actbase = augment_action(&self.Rules[sri].action);
332       if !actbase.ends_with('}') && *passthru>=0 /* && nolhslabel*/  {
333            self.Rules[sri].action = format!("{} _item{}_ }}",&actbase,passthru);
334//println!("passthru on rule {}, NT {}",nri,&self.Rules[nri].lhs.sym);
335       } else if !actbase.ends_with('}') {
336  	    self.Rules[sri].action = format!("{} {}",&actbase,&SACTION);
337	    SAST.push_str(&fields);
338            self.Rules[sri].autogenerated = true;
339       }
340       else  {SAST.push_str(&fields);}            
341       newsa.insert(*nt,(*simplestruct,*canflatten,*passthru,SAST,vfields));
342     }// REAL struct generation loop: apply flatten
343     structasts = newsa;
344     
345
346/////////////////////////////////////// enums generation stage
347
348     // setup hashmap from nt numbers to ASTS
349     let mut enumasts:HashMap<usize,String> = HashMap::new();
350     let mut ASTS = String::from("\n"); // all asts to be generated
351
352     let ltopt = if self.lifetime.len()>0 {format!("<{}>",&self.lifetime)}
353                 else {String::new()};
354     // set of variant group names such as "Binop" that target nt-enum can group
355     let mut groupvariants:HashMap<usize,HashSet<String>> = HashMap::new();
356     
357     //main loop: for each nt and its rules
358     for (nt,NTrules) in self.Rulesfor.iter() // for each nt and its rules
359     {
360        if structasts.contains_key(nt) {continue;}
361        let nti = *nt; 
362        let mut ntsym = &self.Symbols[nti];
363        let willextend = toextend.contains_key(nt);
364        // default for new enum
365	let mut AST = if willextend {String::new()}
366          else {format!("#[derive(Debug)]/*enum*/\npub enum {} {{\n",&ntsym.rusttype)};
367        let NT = &self.Symbols[nti].sym;
368	let mut targetnt = nti;
369	if let Some(ntd) = toextend.get(nt) { targetnt = *ntd;}
370        //set of lhs labels that are variant-group names
371        let groupenums = groupvariants.entry(targetnt).or_default();
372        // group enums are only generated for tuple variants, the presence
373        // of any left or right-side label will cancel its generation
374	// for that particular production/variant
375	for ri in NTrules  // for each rule with NT on lhs
376	{
377          let mut nolhslabel=false;
378          let mut groupoper = ""; // variant-group operator, default none
379          // groupoper cancelled if there is a lhs label
380          if self.Rules[*ri].lhs.label.len()==0 { // no lhs label: make up
381            nolhslabel = true;
382            let mut lhslab = format!("{}_{}",NT,ri); // default
383
384            // search for variant-group operator (only if no lhs label)
385            if self.vargroupnames.len()>0 {
386	     let enti = *toextend.get(&nti).unwrap_or(&nti);
387             for rsym in self.Rules[*ri].rhs.iter() {
388              if let Some(gnamei) = self.vargroups.get(&(enti,rsym.index)) {
389                if groupoper.len()==0 { // not yet set 
390                  lhslab = self.vargroupnames[*gnamei].clone();
391
392		  groupoper = self.Nameslex.get(&rsym.index)
393		              .unwrap_or(&self.Symbols[rsym.index].sym);
394
395                  //groupoper = &self.Symbols[rsym.index].sym;
396                }
397              }// found variant-group operator for current lhs nonterminal
398	      else if let Some(gnamei) = self.vargroups.get(&(usize::MAX,rsym.index)) {
399                if groupoper.len()==0 { // not yet set 
400                  lhslab = self.vargroupnames[*gnamei].clone();
401                  groupoper = &self.Symbols[rsym.index].sym;
402                }
403              }// found variant-group operator for all non-terminals
404	      
405              if rsym.label.len()>0 && !rsym.label.starts_with("_item") {
406                groupoper = ""; // cancel grouping
407                lhslab = format!("{}_{}",NT,ri); // default
408                break;
409              }// group variant canceled
410             }// search for variant-group operator
411            } // if there are variant groups
412
413            if groupoper.len()==0 && self.Rules[*ri].rhs.len()>0 && self.Rules[*ri].rhs[0].terminal {
414	      let symname = &self.Rules[*ri].rhs[0].sym;
415	      if is_alphanum(symname) { //insert r# into enum variant name
416	        lhslab = symname.clone();
417	        if self.Rules[*ri].rhs.len()>1 /*|| self.Rules[*ri].rhs[0].gettype()!="()"*/ { lhslab.push_str(&format!("_{}",ri)); }
418	        }
419  	    }  // determine enum variant name based on 1st rhs symbol
420	    self.Rules[*ri].lhs.label = lhslab;
421          } //nolhslabel
422          let lhsi = self.Rules[*ri].lhs.index; //copy before mut borrow
423	  let lhsymtype = self.Symbols[lhsi].rusttype.clone();
424          let enumname = &self.Symbols[*toextend.get(nt).unwrap_or(nt)].sym;
425
426          // enum action prefix
427	  let mut ACTION =format!("{}::{}",enumname,&self.Rules[*ri].lhs.label);
428          // enumvariant name
429	  let mut enumvar = format!("  {}",&self.Rules[*ri].lhs.label);
430          
431
432          // determine if tuple variant or struct/named variant
433          let mut tuplevariant = true;
434          for rs in &self.Rules[*ri].rhs {
435            if rs.label.len()>0 && !rs.label.starts_with("_item") && !emptybox(&rs.label)
436              { tuplevariant = false; break; }
437          } //determine if tuplevariant
438
439          let mut nullenum = false; // enum variant already exists
440          
441          // form start of enumvariant and action...
442	  if self.Rules[*ri].rhs.len()>0 { // rhs exists
443            if tuplevariant {
444	      enumvar.push('('); ACTION.push('(');
445              if groupoper.len()>0 {
446                if groupenums.contains(&self.Rules[*ri].lhs.label) {
447                  // enum variant type (Binop(&static str,..)) already generated
448                  nullenum = true;
449                } else {
450                  enumvar.push_str("&'static str,");
451		  let toinsert = self.Rules[*ri].lhs.label.clone();
452                  groupenums.insert(toinsert);
453                }
454                ACTION.push_str(&format!("\"{}\",",groupoper));
455              }
456            } else {
457              enumvar.push('{'); ACTION.push('{');
458            }  // struct variant
459	  }//rhsexists
460	  let mut rhsi = 0; // right-side index
461          let mut viadjust = 0;
462	  let mut passthru:i32 = -1; // index of path-thru NT value
463	  for rsym in self.Rules[*ri].rhs.iter_mut()
464	  {
465            let expectedlabel = format!("_item{}_",&rhsi);
466            // check if item has a symbol of the form [x], which forces an
467            // lbox
468            let alreadyislbxlab =
469              rsym.label.len()>1 && rsym.label.starts_with('[') && rsym.label.ends_with(']');
470	    let itemlabel = if rsym.label.len()>0 && &rsym.label!=&expectedlabel && !rsym.label.starts_with('@') {
471            // presence of rhs label also cancels passthru
472              passthru=-2; checkboxexp(&rsym.label,&expectedlabel).to_owned()
473            } else {expectedlabel};
474            
475            if rsym.terminal && rsym.precedence!=0 { passthru = -2; }
476            // Lbox or no Lbox:  ***************
477            let rsymtype = &self.Symbols[rsym.index].rusttype;
478            
479            let mut flattened = false;
480            if !rsym.terminal && flattentypes.contains(&rsym.index) {
481              match structasts.get(&rsym.index) {
482               Some((simp,true,pthr,_,flatfields)) => {  //flatten in
483                if *pthr<0 && /* flatfields.len()>0 && */ !rsym.label.starts_with('['){
484                 flattened=true;
485                 let mut fi = 0;
486                 for (frhsi,flab,albx,ftype) in flatfields {
487                   let newlab = format!("{}_{}",itemlabel,flab);
488                   let newactionlab = if *simp {format!("{}.{}",itemlabel,fi)}
489                       else {format!("{}.{}",itemlabel,flab)};
490                   let newindex = rhsi+viadjust+fi;
491
492                   if tuplevariant {
493                     enumvar.push_str(ftype); enumvar.push(',');
494                     ACTION.push_str(&newactionlab); ACTION.push(',');
495                   } else {
496                     enumvar.push_str(&format!("{}:{},",&newlab,ftype));
497                     ACTION.push_str(&format!("{}:{},",&newlab,&newactionlab));
498                   }//non-tuplevariant
499                   
500                   fi+=1;
501                 }//for each field in flatten source
502                 //viadjust += flatfields.len() -1;
503                }//if can flatten
504               },
505               _ => {},
506              }//match
507              if flattened {rhsi+=1; continue;}
508            }// possible to flatten
509            // not possible to flatten:
510
511            // check if rsym is non-terminal and reaches lsym
512            let lhsreachable = match self.Reachable.get(&rsym.index) {
513               None => false,
514               Some(rset) => rset.contains(&lhsi),
515              };
516
517            let needrecursion = lhsreachable && !nonlbxtype(rsymtype);
518            if alreadyislbxlab || needrecursion {
519	      let mut lclbx = ""; // no lc or lbox needed
520	      let semact;
521              if tuplevariant {
522	        if !self.bumpast && needrecursion {
523                  enumvar.push_str(&format!("LBox<{}>,",rsymtype));
524		  lclbx = "lbx";
525		}
526		else if alreadyislbxlab && !nonlctype(rsymtype) {
527                  enumvar.push_str(&format!("LC<{}>,",rsymtype));
528		  lclbx = "lc";
529		}
530                semact = if lclbx.len()==0 {format!("{},",&itemlabel)} else {format!("parser.{}({},{}),",lclbx,&rhsi, &itemlabel)};
531            } //tuplevariant
532            else { // non-tuple variant
533		if !self.bumpast && needrecursion {
534                  enumvar.push_str(&format!("{}:LBox<{}>,",itemlabel,rsymtype));
535		  lclbx="lbx";
536		} else if alreadyislbxlab && !nonlctype(rsymtype) {
537                  enumvar.push_str(&format!("{}:LC<{}>,",itemlabel,rsymtype));
538		  lclbx = "lc";
539		}
540		semact = if lclbx.len()==0 {format!("{0}:{0},",&itemlabel)} else {format!("{}:parser.{}({},{}),",&itemlabel,lclbx,&rhsi, &itemlabel)};
541//println!("!!!!semact: {}, type: {}", &semact, rsymtype);
542                 } // non-tuple variant
543                 ACTION.push_str(&semact);              
544                 if rsymtype==&lhsymtype && passthru==-1 {passthru=rhsi as i32;}
545                 else {passthru = -2;}
546
547            } // alreadyislbxlabel or need recursion
548
549	    else if rsymtype!="()" || (rsym.label.len()>0 && !rsym.label.starts_with("_item")) {  //no Lbox, include only if non-unit type or has label
550//println!("looking at symbol {}, rusttype {}, label {}",&rsym.sym, &rsym.rusttype, &rsym.label);
551              if tuplevariant {
552                enumvar.push_str(&format!("{},",rsymtype));
553                ACTION.push_str(&format!("{},",&itemlabel));
554              } else {
555                enumvar.push_str(&format!("{}:{},",&itemlabel,rsymtype));
556                ACTION.push_str(&format!("{0}:{0},",&itemlabel));              
557              }// non-tuple variant
558              
559              if rsymtype==&lhsymtype && passthru==-1 {passthru=rhsi as i32;}
560              else {passthru = -2;}
561	    }// could still be nonterminal but not unit type - no lbox
562	    /*
563	    check special case: only one NT on rhs that has same type as lhs,
564	    and all other symbols have type () AND are marked punctuations.
565	    What is a punctuation?  go by precedence level.
566            "paththru" indicates rule like E --> ( E ), where semantic
567            action passes thru.  In this case pasthru will be 1.
568            passthru = -1 means passthru candidate index not yet found,
569            -2 means no passthru candidate exists.
570	    */
571	    rhsi += 1;
572	  }// for each symbol on rhs of rule ri
573          if enumvar.ends_with(',') {
574	      enumvar.pop(); 
575	      if tuplevariant {enumvar.push(')');}
576              else {enumvar.push('}');}
577	      ACTION.pop();
578	      if tuplevariant {ACTION.push(')');}
579              else {ACTION.push('}');}
580	  } else if enumvar.ends_with('(') || enumvar.ends_with('{') {
581	    // this is for the case of no meaningful value,
582	    // but does it cover Binop::"/" ??
583	    enumvar.pop();
584	    ACTION.pop();
585	  }
586	  if ACTION.ends_with('\"') { // for Binaryop("/" ..
587	    if tuplevariant {ACTION.push(')');} else {ACTION.push('}');}
588	  }
589    	  ACTION.push_str(" }");  // action already has last rbrack
590	  // determine if action and ast enum should be generated:
591//          if self.Rules[*ri].action.len()<=1 && passthru>=0 && nolhslabel { // special case
592          let shouldpush = ntsym.rusttype.starts_with(NT) || toextend.contains_key(nt); // auto-generated TYPE (necessarily action)
593          let mut actbase = augment_action(&self.Rules[*ri].action);
594          if !actbase.ends_with('}') && passthru>=0 && nolhslabel {
595            self.Rules[*ri].action = format!("{} _item{}_ }}",&actbase,passthru);
596//println!("passthru on rule {}, NT {}",ri,&self.Rules[*ri].lhs.sym);
597          }
598	  else
599          if !actbase.ends_with('}') && shouldpush {
600  	    self.Rules[*ri].action = format!("{} {}",&actbase,&ACTION);
601	    if !nullenum {AST.push_str(&enumvar); AST.push_str(",\n");}
602            self.Rules[*ri].autogenerated = true;
603	  }
604          else if shouldpush {  // added for 0.2.94
605	    if !nullenum {AST.push_str(&enumvar); AST.push_str(",\n");}
606            //self.Rules[*ri].autogenerated = true;            
607          }
608//println!("Action for rule {}, NT {}: {}",ri,&self.Rules[*ri].lhs.sym,&self.Rules[*ri].action);
609	}// for each rule ri of non-terminal NT
610
611        ////////////////// KEEP ENUM OPEN, INSERT INTO HASHMAP
612        
613        let mut storedAST;
614        if willextend {
615            let targetnti = toextend.get(&nti).unwrap();
616            storedAST = enumasts.remove(targetnti).unwrap_or(String::new());
617            storedAST.push_str(&AST);
618            enumasts.insert(*targetnti,storedAST);            
619        }
620        else {  // check if something already exist, if so add before it
621          storedAST = enumasts.remove(&nti).unwrap_or(String::new());
622          storedAST = format!("{}{}",&AST,&storedAST);
623          enumasts.insert(nti,storedAST);
624        }
625
626     }//for each non-terminal and set of rules (NT, NTRules)
627
628     // Now close all unclosed enums
629     for (nt,ntast) in enumasts.iter() {
630       if !self.Symbols[*nt].rusttype.starts_with(&self.Symbols[*nt].sym) {continue;}
631       if let Some(defaultdef) = self.defaults.get(nt) {
632         let mut ast = format!("{}}}\n",ntast);
633         //println!("cloned {}",&ntast);
634         let uselt = if self.lifetime.len()>0 && self.Symbols[*nt].rusttype.contains(&self.lifetime) {&ltopt} else {""};         
635         ast.push_str(&format!("impl{} Default for {} {{ fn default()->Self {{ use {}::*;  {} }}\n}}\n\n",uselt,&self.Symbols[*nt].rusttype,&self.Symbols[*nt].sym,defaultdef));
636         ASTS.push_str(&ast);         
637       }
638       else
639       if ntast.starts_with("#[derive(Debug)]/*enum*/") { // enum
640       
641 	let defaultvar = format!("{}_Nothing",&self.Symbols[*nt].sym);
642        let mut ast = format!("{}  {},\n}}\n",ntast,&defaultvar);
643        let uselt = if self.lifetime.len()>0 && self.Symbols[*nt].rusttype.contains(&self.lifetime) {&ltopt} else {""};
644	ast.push_str(&format!("impl{} Default for {} {{ fn default()->Self {{ {}::{} }} }}\n\n",uselt,&self.Symbols[*nt].rusttype,&self.Symbols[*nt].sym,&defaultvar));
645        ASTS.push_str(&ast);
646       } // !genstruct - is enum
647       else { ASTS.push_str(ntast); }
648     }// closing all enums and add to ASTS (for loop)
649
650     // set Absyntype
651     self.Absyntype = self.Symbols[self.topsym].rusttype.clone();
652     self.enumhash.insert(self.Absyntype.clone(), 0);
653
654     // now add all the generated struct asts
655     for (_,(_,_,_,Sast,_)) in structasts.iter() {
656       ASTS.push_str(Sast);
657     }
658
659     self.sametype = false;
660     self.ntcxmax = ntcx;
661     ASTS
662   }//prepare_gram
663
664
665   pub fn writeabsyn(&mut self, filename:&str) ->Result<(),std::io::Error>
666   {
667     let ASTS = self.prepare();
668     //let filename = format!("{}_ast.rs",&self.name);
669     let mut fd = File::create(filename)?;
670     write!(fd,"//Abstract syntax types generated by rustlr for grammar {}",&self.name)?;
671     write!(fd,"\n    
672#![allow(unused_variables)]
673#![allow(non_snake_case)]
674#![allow(non_camel_case_types)]
675#![allow(unused_parens)]
676#![allow(unused_imports)]
677#![allow(dead_code)]
678extern crate rustlr;
679pub use rustlr::LC;
680use rustlr::LBox;\n")?;
681//     if self.Extras.len()>0 {write!(fd,"{}\n",&self.Extras)?;}
682     if self.ASTExtras.len()>0 {write!(fd,"\n{}\n",&self.ASTExtras)?;}
683     write!(fd,"{}",&ASTS)?;
684     self.logprint(&format!("Abstract syntax structures created in {}",filename));
685     // add the grammar .extras - these will only be placed in parser file
686     self.Extras.push_str("use rustlr::LBox;\n");
687     //self.Extras.push_str(&format!("use crate::{}_ast;\n",&self.name));
688     self.Extras.push_str(&format!("use crate::{}_ast::*;\n",&self.name));     
689     Ok(())
690   }//writeabsyn
691
692// NOTE including all of Extras (one big string) might cause repeated
693// definitions - best to not include as pubs.
694
695/////  Floyd/Warshall reachability - sort of // new for 0.3.1
696  pub fn reachability(&mut self)
697  {
698     for NT in self.Rulesfor.keys()
699     {
700       self.Reachable.insert(*NT, HashSet::new());
701     } // create map skeletons
702
703     let mut stillopen = true;
704     while stillopen {
705       stillopen = false;
706       for (NT, NTrules) in self.Rulesfor.iter()
707       {
708        //let iNT = *NT; //self.Symhash.get(NT).unwrap();
709        let mut symset = HashSet::new(); // symbols to be added to NT's reach
710        for ri in NTrules
711        {
712           for sym in &self.Rules[*ri].rhs
713           {
714              let symi = sym.index; //*self.Symhash.get(&sym.sym).unwrap();
715              symset.insert(symi);
716              if !sym.terminal { // noterminal
717                 for nsymi in self.Reachable.get(&symi).unwrap().iter()
718                 {
719                     symset.insert(*nsymi);
720                 }
721              }
722           } // collect rhs symbols into a set
723        }//for ri
724        let ireachable = self.Reachable.get_mut(NT).unwrap();
725        for sym in symset
726        {
727          stillopen =  ireachable.insert(sym) || stillopen;
728        }
729       }//(NT,NTrules)
730     }//stillopen
731  }// reachability closure - part 1
732
733  // extend the reachability relation to include type dependencies
734  // assumes that reachability has already been called.
735  pub fn reachability_types(&mut self)
736  {
737     let mut needtoclose = false;
738     for (NT,NTrules) in self.Rulesfor.iter()
739     {
740       let mut ntreach = self.Reachable.get_mut(NT).unwrap();
741       // seed reachable sets with type dependencies like Term : Expr
742       let nttype = &self.Symbols[*NT].rusttype;
743       if nttype.starts_with(':') {
744         if let Some(othernti)=self.Symhash.get(nttype[1..].trim()) {
745	     if ntreach.insert(*othernti) && !needtoclose {needtoclose=true;}
746	     let otherreach=self.Reachable.get_mut(othernti).unwrap();
747	     if otherreach.insert(*NT) && !needtoclose {needtoclose=true;}
748	     // w/r to reachability for ast gen purposes.
749	     // nt reaches othernt because because if bnt-->nt bnt must know
750	     // that nt can reach othernt to calculate lifetime,etc.
751	     // othernt reach nt because, since the cases of nt are included
752	     // as cases under type of othernt, it's as if othernt had more
753	     // productions.
754	 }
755       } // if : starts type
756     } // create map skeletons (for loop)
757     // create closure
758     while needtoclose {
759       needtoclose = false;
760       for NT in self.Rulesfor.keys()
761       {
762        let ireachable1 = self.Reachable.get(NT).unwrap();	
763        let mut symset = HashSet::new(); // symbols to be added to NT's reach
764        for ni in ireachable1.iter() { // for next nt that can be reached
765          if !self.Symbols[*ni].terminal {
766	     let nireachable = self.Reachable.get(ni).unwrap();
767	     for nsymi in nireachable.iter() { symset.insert(*nsymi); }
768	  }
769        }// for each intermediate symbol
770        let ireachable = self.Reachable.get_mut(NT).unwrap(); //re-borrow
771        for sym in symset
772        {
773	  if ireachable.insert(sym) && !needtoclose {needtoclose=true;}
774        }
775       }//(NT,NTrules)
776     }//stillopen, needtoclose
777  }// reachability closure
778
779/*  COMBINED VERSION
780  pub fn reachability(&mut self)
781  {
782     for NT in self.Rulesfor.keys() {
783       self.Reachable.insert(*NT,HashSet::new());
784     }
785     for (NT,NTrules) in self.Rulesfor.iter()
786     {
787       let mut ntreach = self.Reachable.get_mut(NT).unwrap();
788       // seed reachable sets with type dependencies like Term : Expr
789       let nttype = &self.Symbols[*NT].rusttype;
790       if nttype.starts_with(':') {
791         if let Some(othernti)=self.Symhash.get(nttype[1..].trim()) {
792	     ntreach.insert(*othernti);
793	     let otherreach=self.Reachable.get_mut(othernti).unwrap();
794	     otherreach.insert(*NT);  // nt, othernt should be considered same
795	     // w/r to reachability for ast gen purposes.
796	     // nt reaches othernt because because if bnt-->nt bnt must know
797	     // that nt can reach othernt to calculate lifetime,etc.
798	     // othernt reach nt because, since the cases of nt are included
799	     // as cases under type of othernt, it's as if othernt had more
800	     // productions.
801	 }
802       }
803       ntreach = self.Reachable.get_mut(NT).unwrap();       //re-borrow
804       for ri in NTrules // seed based on rhs of rules (just one level)
805        {
806           for sym in &self.Rules[*ri].rhs
807           {
808	      ntreach.insert(sym.index);
809           } // collect rhs symbols into 1st level reachable set
810       }//for ri       
811//       self.Reachable.insert(*NT, ntreach);
812     } // create map skeletons
813     // create closure
814     let mut stillopen = true;
815     while stillopen {
816       stillopen = false;
817       for NT in self.Rulesfor.keys()
818       {
819        let ireachable1 = self.Reachable.get(NT).unwrap();	
820        let mut symset = HashSet::new(); // symbols to be added to NT's reach
821        for ni in ireachable1.iter() { // for next nt that can be reached
822          if !self.Symbols[*ni].terminal {
823	     let nireachable = self.Reachable.get(ni).unwrap();
824	     for nsymi in nireachable.iter() { symset.insert(*nsymi); }
825	  }
826        }// for each intermediate symbol
827        let ireachable = self.Reachable.get_mut(NT).unwrap(); //re-borrow
828        for sym in symset
829        {
830	  if ireachable.insert(sym) && !stillopen {stillopen=true;}
831          //stillopen =  ireachable.insert(sym) || stillopen;
832        }
833       }//(NT,NTrules)
834     }//stillopen
835  }// reachability closure  - combined version
836*/
837}//impl Grammar
838
839
840// function to see if given semantic action should be replaced or augmented
841// returns String base of action, not closed with } if need auto generation.
842fn augment_action(act0:&str) -> String
843{
844   let act = act0.trim();
845   if act.len()<=1 {return String::new();} // completely regenerate
846   let rbpo = act.rfind('}');
847   if let Some(rbp) = rbpo {
848     let ebpo = act[..rbp].rfind("...");
849     if let Some(ebp)=ebpo { 
850        let mut act2 = String::from(&act[..ebp]) + " ";
851        return act2;
852     }
853   }
854   else {return String::new();} // did not end in }
855   return String::from(act);
856}
857
858  // non-LBox types (nor LC)
859fn nonlbxtype(ty:&str) -> bool
860  {
861     ty=="String" || (ty.starts_with('&') && !ty.contains("mut")) || ty.starts_with("Vec<LC") || ty.starts_with("Vec<LBox") || ty.starts_with("LBox") || ty.starts_with("Option<LBox") || ((ty.starts_with("HashMap<") || ty.starts_with("HashSet<") || ty.starts_with("BTree")) && (ty.contains("LBox<") || ty.contains("LC<")))
862  }//nonlbxtype
863
864fn nonlctype(ty:&str) -> bool
865  {
866    ty.starts_with("LC<") || ty.starts_with("LBox<") || ty.starts_with("Vec<LC") || ty.starts_with("Vec<LBox") || ty.starts_with("Option<LBox") || ty.starts_with("Option<LC")
867  }