    //original
    fn parse0(&mut self, tokenizer:&mut dyn Lexer<AT>) -> AT
    {
       self.err_occurred = false;
       self.stack.clear();
       let mut eofcount = 0;
//       self.exstate = ET::default(); ???
       let mut result = AT::default();
       // push state 0 on stack:
       self.stack.push(Stackelement {si:0, value:AT::default()});
       let unexpected = Stateaction::Error("unexpected end of input");
       let mut action = unexpected; 
       self.stopparsing = false;
       let mut lookahead = Lextoken{sym:"EOF".to_owned(),value:AT::default()}; 
       if let Some(tok) = tokenizer.nextsym() {lookahead=tok;}
       else {self.stopparsing=true;}

       while !self.stopparsing
       {
         self.linenum = tokenizer.linenum(); self.column=tokenizer.column();
         let currentstate = self.stack[self.stack.len()-1].si;
         //if TRACE>1 {print!(" current state={}, lookahead={}, ",&currentstate,&lookahead.sym);}
         let mut actionopt = self.RSM[currentstate].get(lookahead.sym.as_str());//.unwrap();
//         if TRACE>1 {println!("RSM action : {:?}",actionopt);}
//println!("actionopt: {:?}, current state {}",actionopt,self.stack[self.stack.len()-1].si);            

///// Do error recovery
         if iserror(&actionopt) /*let None = actionopt*/ {
//            self.report(&format!("unexpected symbol {} ... current state {}",&lookahead.sym,self.stack[self.stack.len()-1].si));
            let lksym = &lookahead.sym[..];
            // is lookahead recognized as a grammar symbol?
            // if actionopt is NONE, check entry for ANY_ERROR            
            if self.Symset.contains(lksym) {
               if let None=&actionopt {
                  actionopt = self.RSM[currentstate].get("ANY_ERROR");
               }
            }// lookahead is recognized grammar sym
            else {
               actionopt = self.RSM[currentstate].get("ANY_ERROR");
            }// lookahead is not a grammar sym

            let errmsg = if let Some(Error(em)) = &actionopt {
               format!("unexpected symbol {}, ** {} ** ..",lksym,em)
            } else {format!("unexpected symbol {} ..",lksym)};

            self.report(&errmsg);
            
            if self.training {  /////// TRAINING MODE:
              let cstate = self.stack[self.stack.len()-1].si;
              let csym = lookahead.sym.clone();
              let mut inp = String::from("");
              print!("\n>>>TRAINER: is this error message adequate? If not, enter a better one: ");
              let rrrflush = io::stdout().flush();
              if let Ok(n) = io::stdin().read_line(&mut inp) {
                if inp.len()>5 && self.Symset.contains(lksym) /*&& !self.trained.contains_key(&(cstate,csym.clone()))*/ {
                  print!(">>>TRAINER: should this message be given for all unexpected symbols in the current state? (default yes) ");
                  let rrrflush2 = io::stdout().flush();
                  let mut inp2 = String::new();
                  if let Ok(n) = io::stdin().read_line(&mut inp2) {
                     if inp2.trim()=="no" || inp2.trim()=="No" {
                       self.trained.insert((cstate,csym),inp);
                     }
                     else  {// insert for any error
                       self.trained.insert((cstate,String::from("ANY_ERROR")),inp);
                     }
                  }// read ok
                }// unexpected symbol is grammar sym
                else if inp.len()>5 && !self.Symset.contains(lksym) /*&& !self.trained.contains_key(&(cstate,String::from("ANY_ERROR")))*/ {
                  self.trained.insert((cstate,String::from("ANY_ERROR")),inp);
                }
                
 /*               
                if n>2 && !self.trained.contains_key(&(cstate,csym.clone())) {
                  self.trained.insert((cstate,csym),inp);
                }
*/                
              }// process user response
            }//train   //// END TRAINING MODE
            

      // do error recovery
            let mut erraction = None;

            ///// prefer to use Errsym method
            if self.Errsym.len()>0 {
               let errsym = self.Errsym;
               //lookdown stack for "shift" action on errsym
               // but that could be current state too (start at top)
               let mut k = self.stack.len(); // offset by 1 because of usize
               let mut spos = k+1;
               while k>0 && spos>k
               {
                  let ksi = self.stack[k-1].si;
                  erraction = self.RSM[ksi].get(errsym);
                  if let None = erraction {k-=1;} else {spos=k;}
                  //if let Some(Shift(_)) = erraction { spos=k;}
                  //else {k-=1;}
               }//while k>0
               if spos==k { self.stack.truncate(k); }

            // run all reduce actions that are valid before the Errsym:
            while let Some(Reduce(ri)) = erraction // keep reducing
            {
              //self.reduce(ri); // borrow error- only need mut self.stack
              let rulei = &self.Rules[*ri];
              let ruleilhs = rulei.lhs; // &'static : Copy
//println!("ERR reduction on rule {}, lhs {}",ri,ruleilhs);
              let val = (rulei.Ruleaction)(self); // calls delegate function
              let newtop = self.stack[self.stack.len()-1].si; 
              let gotonopt = self.RSM[newtop].get(ruleilhs);
              match gotonopt {
                Some(Gotonext(nsi)) => { 
                  self.stack.push(Stackelement{si:*nsi,value:val});
                },// goto next state after reduce
                _ => {self.abort("recovery failed"); },
              }//match
              // end reduce
              let tos=self.stack[self.stack.len()-1].si;
              erraction = self.RSM[tos].get(self.Errsym);
            } // while let erraction is reduce


               if let Some(Shift(i)) = erraction { // simulate shift errsym 
                 self.stack.push(Stackelement{si:*i,value:AT::default()});
//println!("SIMULATING shift to state {}",i);                 
                 // keep lookahead until action is found that transitions from
                 // current state (i). but skipping ahead without reducing
                 // the error production is not a good idea
                 while let None = self.RSM[*i].get(&lookahead.sym[..]) {
                    if &lookahead.sym[..]=="EOF" {eofcount+=1; break;}
                    lookahead = self.nexttoken(tokenizer);
                 }//while let
                 // either at end of input or found action on next symbol
                 erraction = self.RSM[*i].get(&lookahead.sym[..]);
//println!("next action from state {} on lookahead {} : {:?}",i,&lookahead.sym,&erraction);                 
               } // if shift action found down under stack
               //else {erraction = None; }// don't reduce
            }//errsym exists

            // at this point, if erraction is None, then Errsym failed to recover,
            // try the resynch symbol method...
            
            if erraction==None && self.resynch.len()>0 {
               while &lookahead.sym!="EOF" &&
                      !self.resynch.contains(&lookahead.sym[..]) {
                 lookahead = self.nexttoken(tokenizer);
               }
             if &lookahead.sym!="EOF" {
              // look for state on stack that has action defined on next symbol
              lookahead = self.nexttoken(tokenizer); // skipp err-causing symbol
             }
             else {eofcount += 1;}
              let mut k = self.stack.len()-1; // offset by 1 because of usize
              let mut position = 0;
              while k>0 && erraction==None
               {
                  let ksi = self.stack[k-1].si;
                  erraction = self.RSM[ksi].get(&lookahead.sym[..]);
                  if let None=erraction {k-=1;}
               }//while k>0 && erraction==None
              match erraction {
                 None => {}, // do nothing, whill shift next symbol
                 _ => { self.stack.truncate(k);},//pop stack
              }//match
            }// there are resync symbols

            // at this point, if erraction is None, then resynch recovery failed too.
            // only action left is to skip ahead...
            if let None = erraction { //skip input, loop back
                lookahead = self.nexttoken(tokenizer);
                let csi =self.stack[self.stack.len()-1].si;
                erraction = self.RSM[csi].get(&lookahead.sym[..]);
//println!("csi {}",csi);                
                if &lookahead.sym=="EOF" && erraction==None && eofcount>0 {
                  self.abort("error recovery failed before end of input");
                }
            }

/* /////           
            while let Some(Reduce(ri)) = erraction // keep reducing
            {
              //self.reduce(ri); // borrow error- only need mut self.stack
              let rulei = &self.Rules[*ri];
              let ruleilhs = rulei.lhs; // &'static : Copy
              let val = (rulei.Ruleaction)(self); // calls delegate function
              let newtop = self.stack[self.stack.len()-1].si; 
              let gotonopt = self.RSM[newtop].get(ruleilhs);
              match gotonopt {
                Some(Gotonext(nsi)) => { 
                  self.stack.push(Stackelement{si:*nsi,value:val});
                },// goto next state after reduce
                _ => {self.abort("recovery failed"); },
              }//match
              // end reduce
              let tos=self.stack[self.stack.len()-1].si;
              erraction = self.RSM[tos].get(self.Errsym);
            } // while let erraction is reduce
            //println!("erraction: {:?}, current state {}",erraction,self.stack[self.stack.len()-1].si);

///// */

         }//error recovery
         
         else {
          action = actionopt.unwrap().clone();  // cloning stateaction is ok
          match &action {
            Stateaction::Shift(i) => { // shift to state si
                self.stack.push(Stackelement{si:*i,value:mem::replace(&mut lookahead.value,AT::default())});
                lookahead = self.nexttoken(tokenizer);
             }, //shift
            Stateaction::Reduce(ri) => { //reduce by rule i
               self.reduce(ri);
            /*
              let rulei = &self.Rules[*ri];
              let ruleilhs = rulei.lhs; // &'static : Copy
              let val = (rulei.Ruleaction)(self); // calls delegate function
              let newtop = self.stack[self.stack.len()-1].si; 
              let goton = self.RSM[newtop].get(ruleilhs).unwrap();
              if let Stateaction::Gotonext(nsi) = goton {
                self.stack.push(Stackelement{si:*nsi,value:val});
                // DO NOT CHANGE LOOKAHEAD AFTER REDUCE!
              }// goto next state after reduce
              else { self.stopparsing=true; }
             */
             },
            Stateaction::Accept => {
              result = self.stack.pop().unwrap().value;
              self.stopparsing = true;
             },
            Stateaction::Error(msg) => {
              self.stopparsing = true;
             },
            Stateaction::Gotonext(_) => { //should not see this here
              self.stopparsing = true;
             },
          }//match & action
         }// else not in error recovery mode
       } // main parser loop
       if let Stateaction::Error(msg) = &action {
          //panic!("!!!Parsing failed on line {}, next symbol {}: {}",tokenizer.linenum(),&lookahead.sym,msg);
          self.report(&format!("failure with next symbol {}",tokenizer.linenum()));
       }
       //if self.err_occurred {result = AT::default(); }
       return result;
    }//parse0

//////////////

/// This type is retained for compatibility with existing parsers but
/// but is deprecated by the [ErrHandler] trait.
pub type ErrorReporter<AT,ET> =
  fn(&mut RuntimeParser<AT,ET>, &Lextoken<AT>, &Option<Stateaction>);
  

//impl<AT:Default,ET:Default> RuntimeParser<AT,ET>
//{

  // shift/reduce already implemented
  // no separate function for gotonext - part of reduce

  /// This is the core parser, which expects a ErrorReporter function to be
  /// passed in as an argument.  *This function is being deprecated in favor
  /// of [RuntimeParser::parse_base]*.
  pub fn parse_core(&mut self, tokenizer:&mut dyn Lexer<AT>, err_reporter:ErrorReporter<AT,ET>) -> AT
  {
    self.stack.clear();
    self.err_occurred = false;
    let mut result = AT::default();
    self.stack.push(Stackelement {si:0, value:AT::default()});
    self.stopparsing = false;
    let mut action = Stateaction::Error("");
    let mut lookahead = Lextoken{sym:"EOF".to_owned(),value:AT::default()};
    if let Some(tok) = tokenizer.nextsym() {lookahead=tok;}
    else {self.stopparsing=true;}

    while !self.stopparsing
    {
      self.linenum = tokenizer.linenum(); self.column=tokenizer.column();
      let currentstate = self.stack[self.stack.len()-1].si;
      let mut actionopt = self.RSM[currentstate].get(lookahead.sym.as_str());
      let actclone:Option<Stateaction> = match actionopt {
        Some(a) => Some(*a),
        None => None,
      };
      if iserror(&actionopt) {  // either None or Error
        if !self.err_occurred {self.err_occurred = true;}
        err_reporter(self,&lookahead,&actclone);
        match self.error_recover(&mut lookahead,tokenizer) {
          None => { self.stopparsing=true; break; }
          Some(act) => {action = act;},
        }//match
      }// iserror
      else { action = actclone.unwrap(); }
      match &action {
        Shift(nextstate) => {
           lookahead = self.shift(*nextstate,lookahead,tokenizer);
        },
        Reduce(rulei) => { self.reduce(rulei); },
        Accept => {
          self.stopparsing=true;
          if self.stack.len()>0 {result = self.stack.pop().unwrap().value;}
          else {self.err_occurred=true;}
        },
        _ => {}, // continue
      }//match action
    }// main parse loop
    return result;
  }//parse_core

//
/////

/// default ErrorReporter, with training ability
fn err_report_train<AT:Default,ET:Default>(parser:&mut RuntimeParser<AT,ET>, lookahead:&Lextoken<AT>, erropt:&Option<Stateaction>)
{
  // known that actionop is None or Some(Error(_))
  let cstate = parser.stack[parser.stack.len()-1].si;
  let mut actionopt = if let Some(act)=erropt {Some(act)} else {None};
  let lksym = &lookahead.sym[..];
  // is lookahead recognized as a grammar symbol?
  // if actionopt is NONE, check entry for ANY_ERROR            
  if parser.Symset.contains(lksym) {
     if let None=actionopt {
        actionopt = parser.RSM[cstate].get("ANY_ERROR");
     }
  }// lookahead is recognized grammar sym
  else {
     actionopt = parser.RSM[cstate].get("ANY_ERROR");
  }// lookahead is not a grammar sym
  let errmsg = if let Some(Error(em)) = &actionopt {
    format!("unexpected symbol {}, ** {} ** ..",lksym,em.trim())
  } else {format!("unexpected symbol {} .. ",lksym)};

  parser.report(&errmsg);
         
  if parser.training {  /////// TRAINING MODE:
    let cstate = parser.stack[parser.stack.len()-1].si;
    let csym = lookahead.sym.clone();
    let mut inp = String::from("");
    print!("\n>>>TRAINER: if this message is not adequate (for state {}), enter a replacement (default no change): ",cstate);
    let rrrflush = io::stdout().flush();
    if let Ok(n) = io::stdin().read_line(&mut inp) {
       if inp.len()>5 && parser.Symset.contains(lksym) {
         print!(">>>TRAINER: should this message be given for all unexpected symbols in the current state? (default yes) ");
        let rrrflush2 = io::stdout().flush();
        let mut inp2 = String::new();
        if let Ok(n) = io::stdin().read_line(&mut inp2) {
            if inp2.trim()=="no" || inp2.trim()=="No" {
               parser.trained.insert((cstate,csym),inp);
            }
            else  {// insert for any error
                       parser.trained.insert((cstate,String::from("ANY_ERROR")),inp);
            }
        }// read ok
       }// unexpected symbol is grammar sym
       else if inp.len()>5 && !parser.Symset.contains(lksym) {
         parser.trained.insert((cstate,String::from("ANY_ERROR")),inp);
       }
    }// process user response
  }//if training   //// END TRAINING MODE
}// default errorreporter function - conforms to type ErrorReporter (older)



/*  old versions of file augmenter
// old version kept in case new one doesn't work on some file systems.
// function to read file and agument  // original version
fn augment_file0<AT:Default,ET:Default>(filename:&str, parser:&mut RuntimeParser<AT,ET>) -> std::io::Result<()>
{
   let outfile = format!("augmented_{}",filename);
   let finopt = match File::open(filename) {
     Ok(f) => { Some(BufReader::new(f)) },
     _ => { return Err(Error::new(ErrorKind::Other,"can't find file")); }
   };
   let mut fin = finopt.unwrap();
   let mut fout = File::create(outfile)?;
   let mut line = String::new();
   fin.read_line(&mut line)?;
   if line.trim()!="//Parser generated by rustlr" {
     return Err(Error::new(ErrorKind::Other, "input file was not created by rustlr"));
   }
   write!(fout,"{}",&line)?;
   let mut stop = false;
   let mut oktoaugment = true;
   while !stop
   {
     line = String::new();
     match fin.read_line(&mut line) {
       Ok(n) if n>0 => {},
       _ => {stop=true; oktoaugment=false;}
     }
     if line.trim().len()>21 && &line.trim()[..21]=="}//end of load_extras"  {stop=true;}
     else {
        write!(fout,"{}",&line)?;
     }
   }//while !stop
   //// now augment
   if oktoaugment {
//println!("AUGMENTATION STARTED");   
    for key in parser.trained.keys()
    {
     let (state,sym) = key;
     let enter = parser.trained.get(key).unwrap().trim();
     write!(fout,"  parser.RSM[{}].insert(\"{}\",Stateaction::Error(\"{}\"));\n",state,sym,enter)?;
    }
   write!(fout,"}}//end of load_extras: don't change this line as it affects augmentation\n")?;
  }
  else {return Err(Error::new(ErrorKind::Other,"given file cannot be augmented"));}
   Ok(())
}//augment_file


//   "//Parser generated by rustlr"
//}//end of load_extras: don't change this line as it affects augmentation



////////////////////////////////////////////////////////////////////
/////////////////// version 2: writes to same file

pub fn augment_file<AT:Default,ET:Default>(filepath:&str, parser:&mut RuntimeParser<AT,ET>) -> std::io::Result<()>
{
   if parser.trained.len()<1 {return Ok(());}
   let fopen = std::fs::OpenOptions::new().write(true).read(true).open(filepath);
   match &fopen   {
     Ok(f) => {},
     _ => {
       return Err(Error::new(ErrorKind::Other,"augmenter can't find file"));
     },
   }//match
   let mut fio = fopen.unwrap();
   let finopen = File::open(filepath);
   if let Err(_) = finopen {   return augment_file0(filepath,parser);   }
   let mut fin = BufReader::new(finopen.unwrap());
   let mut position:u64 = 0;
   let mut line = String::new();
   fin.read_line(&mut line)?;
   if line.trim()!="//Parser generated by rustlr" {
     return Err(Error::new(ErrorKind::Other, "input file was not created by rustlr"));
   }
   //write!(fout,"{}",&line)?;
   let mut stop = false;
   let mut oktoaugment = true;
   while !stop
   {
     line = String::new();
     position = fin.stream_position()?;     
     match fin.read_line(&mut line) {
       Ok(n) if n>0 => {},
       _ => {stop=true; oktoaugment=false;}
     }
     if line.trim().len()>21 && &line.trim()[..21]=="}//end of load_extras"  {stop=true;}
   }//while !stop
   //// now augment
   if oktoaugment {
    fio.seek(SeekFrom::Start(position))?;
    for key in parser.trained.keys()
    {
     let (state,sym) = key;
     let enter = parser.trained.get(key).unwrap().trim();
     write!(fio,"  parser.RSM[{}].insert(\"{}\",Stateaction::Error(\"{}\"));\n",state,sym,enter)?;
    }
   write!(fio,"}}//end of load_extras: don't change this line as it affects augmentation\n")?;
  } //ok to augment
  else {return Err(Error::new(ErrorKind::Other,"given file cannot be augmented"));}
   Ok(())
}// new augment_file
*/

