/*
  Copyright Dave Bone 1998 - 2014 
  All Rights Reserved. 
  No part of this document may be reproduced without written consent from the author.
	
FILE:		mpost_output.lex
Dates:		29 Nov. 2005
Purpose:	Create Pascal type railroad diagrams per grammar rule.
			The output file is code for Mpost to generate the diagrams.
*/
/@
@i "/usr/local/yacco2/copyright.w"
@** |mpost_output| Grammar.\fbreak
Write out the |cweb|'s representation of the grammar's code
and |mpost|'s railroad diagrams.
These appropriate files have the grammar's name along 
with extension ``w'' for
|cweave| to compile and extension ``mp'' for |mpost| to draw from.

Enjoy the art work. All the grammar's vocabulary are cross referenced
to accomodate the grammar writer. 
The railroad diagrams show left recursion and
where the subrule has appropriate syntax directed code, it redraws the 
subrule with each element's position number below it.
This aids in the writing of syntax directed code
when individual parameters are referenced 
 by use of the |sf| stack frame structure.
Each element within the stack frame has a naming convention of
|p#__| where the number sign is the element's 
position within the subrule sentence relative to 1.\fbreak
\fbreak
Fodder to this grammar is provided by
the tree container doing a prefix traverse.
Why a prefix traverse?
The parent node allowed me to declare
the |mpost| variables before their use.\fbreak
  
Caveat: Grammar c++ comments must observe |cweave|
dictums: underscored items like variable names must
be enclosed with ${\vert}$ and the number sign
``\#'' must use its literal of backslash \#. Without
these observances, |pdftex| will honk.


@/
fsm	
(fsm-id "mpost_output.lex",fsm-filename mpost_output,fsm-namespace NS_mpost_output
,fsm-class Cmpost_output{
  constructor
    MPOST_CWEB_LOAD_XLATE_CHRS(this);
  ***
user-prefix-declaration
#include "time.h"
#include "o2_externs.h"
extern void XLATE_SYMBOLS_FOR_cweave(const char* Sym_to_xlate,char* Xlated_sym);
extern void PRT_RULE_S_FIRST_SET(std::ofstream& Ofile,NS_yacco2_terminals::rule_def* Rule_def);
extern int MPOST_CWEB_xlated_symbol(AST* Sym,char* Xlated_sym);
extern STATES_type LR1_STATES;
extern COMMON_LA_SETS_type COMMON_LA_SETS;
#include "cweave_fsm_sdc.h"
#include "cweave_lhs_sdc.h"
#include "cweave_sdc.h"
#include "cweave_T_sdc.h"
  ***
  user-declaration
    public:
	  char big_buf_[BIG_BUFFER_32K];		
	  std::map <int , std::string> xlated_names_;
	  std::vector <int> no_subrules_per_rule_;
	  std::string gened_date_time_;
      std::string mp_filename_;
      std::ofstream omp_file_;
      std::string w_filename_;
      std::string w_index_filename_;
      std::ofstream ow_file_;
      std::ofstream ow_index_file_;
      std::ofstream ow_t_file_;
      std::ofstream ow_err_file_;
      std::ofstream ow_lrk_file_;
      std::string grammar_filename_prefix_;
	  std::string fq_filename_noext_;
      int w_fig_no_;
      int rule_no_;
      int subrule_no_;
      int elem_no_;
      int no_of_rules_;
      int no_of_subrules_;
      std::string rule_name_;
      std::string elem_name_;
	  rule_def* rule_def_;
	  T_subrule_def* subrule_def_;
      std::string mp_dimension_;
   static void MPOST_CWEB_gen_dimension_name(Cmpost_output* Fsm
	,std::string& Mp_obj_name,int Dimension);
   static void MPOST_CWEB_calc_mp_obj_name(Cmpost_output* Fsm
	,std::string& Mp_obj_name,int Elem_no);
   static void MPOST_CWEB_wrt_mp_rhs_elem(Cmpost_output* Fsm
	,std::string& Elem_name,std::string& Drw_how);
   static void MPOST_CWEB_gen_sr_elem_xrefs(Cmpost_output* Fsm
	,AST* Subrule_tree);   
   static void MPOST_CWEB_woutput_sr_sdcode(Cmpost_output* Fsm
	,T_subrule_def* Subrule_def);
   static void MPOST_CWEB_wrt_fsm(Cmpost_output* Fsm
	,T_fsm_phrase* Fsm_phrase);
   static void MPOST_CWEB_wrt_rule_s_lhs_sdc(Cmpost_output* Fsm,
	rule_def* Rule_def);
   static bool MPOST_CWEB_should_subrule_be_printed(Cmpost_output* Fs
	,T_subrule_def* Subrule_def);
   static void MPOST_CWEB_xref_refered_T(Cmpost_output* Fsm
	,refered_T* R_T);
   static void MPOST_CWEB_xref_refered_rule(Cmpost_output* Fsm
	,refered_rule* R_rule);
   static void MPOST_CWEB_LOAD_XLATE_CHRS(Cmpost_output* Fsm);
   static void MPOST_CWEB_EMIT_PREFIX_CODE(Cmpost_output* Fsm);
   static void MPOST_CWEB_wrt_T(Cmpost_output* Fsm
	,T_terminals_phrase* T_phrase);
   static void MPOST_CWEB_wrt_Err(Cmpost_output* Fsm
	,T_error_symbols_phrase* Err_phrase);
   static void MPOST_CWEB_wrt_Lrk(Cmpost_output* Fsm
	,T_lr1_k_phrase* Lrk_phrase);
   std::list<NS_yacco2_terminals::rule_def*> rules_for_fs_prt_;
   static void MPOST_CWEB_crt_rhs_sym_str(state_element* se,std::string* Xlated_str);
  ***
/@
Due to a MS 2005 Visual Studio defficiency,
part of the ctor's initialization is included here 
instead of all being done by
|MPOST_CWEB_EMIT_PREFIX_CODE|.
The reason is noted in the documentation of |o2externs|.
@/  
  op
no_subrules_per_rule_.push_back(0);
time_t theTime= time(0);
char*cTime= ctime(&theTime);
gened_date_time_+= string(cTime);
int n= gened_date_time_.find('\n');
gened_date_time_[n]= ' ';

mp_dimension_+= " abcdefghijklmnopqrstuvwxyz";
w_fig_no_= 0;
rule_def_= 0;
subrule_def_= 0;
rule_no_= 0;
subrule_no_= 0;
elem_no_= 0;
no_of_rules_= 0;
no_of_subrules_= 0;
mp_filename_+= grammar_filename_prefix_.c_str();
mp_filename_+= ".mp";
omp_file_.open(mp_filename_.c_str(),ios_base::out|ios::trunc);
if(!omp_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(mp_filename_.c_str());
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}
w_filename_+= grammar_filename_prefix_.c_str();
w_filename_+= ".w";
ow_file_.open(w_filename_.c_str(),ios_base::out|ios::trunc);
if(!ow_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(w_filename_.c_str());
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}
w_index_filename_+= grammar_filename_prefix_.c_str();
w_index_filename_+= "_idx.w";
ow_index_file_.open(w_index_filename_.c_str(),ios_base::out|ios::trunc);
if(!ow_index_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(w_index_filename_.c_str());
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}


KCHARP t_file = "T.w";
ow_t_file_.open(t_file,ios_base::out|ios::trunc);
if(!ow_t_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(t_file);
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}
KCHARP err_file = "Err.w";
ow_err_file_.open(err_file,ios_base::out|ios::trunc);
if(!ow_err_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(err_file);
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}

KCHARP lrk_file = "Lrk.w";
ow_lrk_file_.open(lrk_file,ios_base::out|ios::trunc);
if(!ow_lrk_file_){
CAbs_lr1_sym*sym= new Err_bad_filename(lrk_file);
      sym->set_who_created(__FILE__,__LINE__);
parser__->add_token_to_error_queue(*sym);
parser__->set_stop_parse(true);
return;
}

	MPOST_CWEB_EMIT_PREFIX_CODE(this);
  ***
}
,fsm-version "1.0",fsm-date "29 Nov. 2005",fsm-debug "false"
,fsm-comments "Output grammar rules railroad diagrams for mpost that cweb program uses.")
@"/usr/local/yacco2/compiler/grammars/yacco2_T_includes.T"

rules{
Rmpost_output  

/@
Note how i use the sentence recognition to 
complete |mpost| figures.
The individual figures are defined before their execution.
@/
(){
  -> Rgrammar_phrase 
	 Rks_epi
     Rfsm_phrase 
	 Rks_epi
     Rparallel_phrase 
	 Rks_epi
     RT_enum_phrase 
	 Rks_epi
     Rlr1_k_phrase 
	 Rks_epi
     Rrc_phrase 
	 Rks_epi
	 Rterms_phrase
	 Rks_epi
     Rerr_phrase 
	 Rks_epi
	 Rrule_phrase 
	 Rrules 
     Rks_epi
	 eog {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
	KCHARP mp_init_vars =	"init_variables;";
 	fsm->omp_file_ << mp_init_vars << endl;
	KCHARP mp_figure =	
		"beginfig(%i);\n"
		"  drw_rule(%i);\n"
		"endfig;";
	KCHARP sr_figure =	
		"beginfig(%i);\n"
		"  drw_rule_a_rhs(%i,%i);\n"
		"endfig;";

	int rule_no = 1;
	int fig_no(0);
	for(;rule_no <= fsm->no_of_rules_;++rule_no){
		++fig_no;
 	    sprintf(fsm->big_buf_,mp_figure,fig_no,rule_no);
 	    fsm->omp_file_ << fsm->big_buf_ << endl;
 	    int no_srs = fsm->no_subrules_per_rule_[rule_no];
 	    int sr_no(1);
 	    for(;sr_no <= no_srs;++sr_no){
 	      ++fig_no;
 	      sprintf(fsm->big_buf_,sr_figure,fig_no,rule_no,sr_no);
 	      fsm->omp_file_ << fsm->big_buf_ << endl;
 	    }
	}

	KCHARP mp_end =	"end;";
 	fsm->omp_file_ << mp_end << endl;
 	fsm->omp_file_.close();
	KCHARP w_fsc_file =	
    "@** First Set Language for \\Olinker.\\fbreak\n"
 	"\\openin 1 = \"%s.fsc\"\n" // macro file open
	"\\ifeof 1 true\n" // test the result
	 " \\closein 1\n"
	"\\else\n"   
	"   \\closein 1\n"
        "   \\listing{\"%s.fsc\"}"
	"\\fi";
 	sprintf(fsm->big_buf_,w_fsc_file,fsm->fq_filename_noext_.c_str()
		,fsm->fq_filename_noext_.c_str());
    fsm->ow_file_ << fsm->big_buf_ << endl;

    KCHARP w_lr1_states =	
        "@** Lr1 State Network.\\fbreak\n"
        "\\statecolumnspace %s";
    sprintf(fsm->big_buf_,w_lr1_states," ");
    fsm->ow_file_ << fsm->big_buf_ << endl;
    KCHARP w_stateno = // stateno, vectored into symbol, conflict	
        "\\stateno{%i} {%s} {%s}\n"
        "\\halign{\n"
        "\\span\\statetemplate\n"
        "\\statetitle";
  char vectored_into[Max_cweb_item_size];
  char rule_name[Max_cweb_item_size];
  char subrule_sym[Max_cweb_item_size];
  char cur_sym[Max_cweb_item_size];
  char nxtsym_1[Max_cweb_item_size];
  char nxtsym_2[Max_cweb_item_size];
  char state_type[Max_cweb_item_size];
  char la_set_no[Max_cweb_item_size];
  string w_possible_ar_code;

  KCHARP la_set_no_template = "%i";
  KCHARP state_type_template = "%s";
  KCHARP three_symbols_string_template = 
        "%s %s %s";        // 3 symbols: current and 2 la
  KCHARP w_stateno_subrule = 
        "{%s}&\n"        // closure or transitive
        "{%s}&\n"       // rule
        "{%i\\hss}&\n"   // rule no
        "{%i\\hss}&\n"   // subrule no
        "{%i\\hss}&\n"   // element pos
        "{%s}&\n"       // current symbol
        "{%i\\hss}&\n"   // born state no
        "{%i\\hss}&\n"   // goto state no
        "{%i\\hss}&\n"   // reduced state no
        "{%s\\hss}\\cr";   // la set if reduced
  int fnd_reduced_stateno(0);
  
  STATES_ITER_type si = LR1_STATES.begin();
  STATES_ITER_type sie = LR1_STATES.end();
  for(;si!=sie;++si){// walk the states
    vectored_into[0] = (char)0;
    w_possible_ar_code.clear();
    state_type[0] = (char)0;
    state* cur_state = *si;
    
    if(cur_state->state_no_ != 1){// state 1 vector into is empty
      XLATE_SYMBOLS_FOR_cweave
            (cur_state->entry_symbol_literal(),vectored_into);
    }
        switch (cur_state->state_type_){
           case 0:{//shift only
             sprintf(state_type,state_type_template,"\\Shiftonly");
             break;
           }
           case 1:{
             sprintf(state_type,state_type_template,"\\Reduceonly");
             break;
           }
           case 2:{
             sprintf(state_type,state_type_template,"\\ShiftReduce");
             break;
           }
           case 3:{
             sprintf(state_type,state_type_template,"\\MultipleReduces");
             break;
           }
           case 4:{
             sprintf(state_type,state_type_template,"\\ShiftandMultipleReduces");
             break;
           }
        }
    w_possible_ar_code.append(vectored_into);
    if (cur_state->vectored_into_by_elem_ == LR1_PARALLEL_OPERATOR){
     // add arbitration code? to |vectored_into| string
     if (cur_state->arbitrator_name_ != 0){
      w_possible_ar_code += "\\ arbitration-code: \\ |";
      w_possible_ar_code += cur_state->arbitrator_name_->c_str();
      w_possible_ar_code += "|";
     }else{
      w_possible_ar_code += "\\ arbitration-code: \\ \\epsilon";
     }
    }
    sprintf(fsm->big_buf_
            ,w_stateno
            ,cur_state->state_no_
            ,w_possible_ar_code.c_str()//|vectored_into|
            ,state_type);
    fsm->ow_file_ << fsm->big_buf_ << endl;
	S_VECTORS_ITER_type svi = cur_state->state_s_vector_.begin();
	S_VECTORS_ITER_type svie = cur_state->state_s_vector_.end();
        string rhs_syms_str;
	for(;svi!=svie;++svi){
	  S_VECTOR_ELEMS_ITER_type seli = svi->second.begin();
	  S_VECTOR_ELEMS_ITER_type selie = svi->second.end();
	  for(;seli!=selie;++seli){
	        rhs_syms_str.clear();     
		state_element* se = *seli;
                rule_def* rd = se->subrule_def_->its_rule_def();
                T_subrule_def* srd = se->subrule_def_;
                rule_name[0] = (char)0;
                subrule_sym[0] = (char)0;
                cur_sym[0] = (char)0;
                la_set_no[0] = (char)0;

                XLATE_SYMBOLS_FOR_cweave(rd->rule_name()->c_str(),rule_name);
                int elem_pos(0);
                if(se->previous_state_ == 0) elem_pos = 1;
                elem_pos = MPOST_CWEB_xlated_symbol(se->sr_element_,cur_sym);
                if(se->goto_state_==0){// reducing subrule
                 sprintf(la_set_no,la_set_no_template,se->common_la_set_idx_+1);
		}else{// no la set
 		}
   
  if (se->reduced_state_ != 0) {
    fnd_reduced_stateno = se->reduced_state_->state_no_;
    }
  else{ // find from merged into
    fnd_reduced_stateno=-1;
    if (se->next_state_element_->reduced_state_ != 0){
     fnd_reduced_stateno = se->next_state_element_->reduced_state_->state_no_;   
    }
  }
		fsm->MPOST_CWEB_crt_rhs_sym_str(se,&rhs_syms_str);                
                sprintf(fsm->big_buf_
                    ,w_stateno_subrule
                    ,(elem_pos == 1)?"c":"t"
                    ,rule_name
                    ,rd->rule_no()
                    ,srd->subrule_no_of_rule()
                    ,elem_pos
                    ,rhs_syms_str.c_str()
                    ,se->closure_state_->state_no_
                    ,(se->goto_state_ !=0)?se->goto_state_->state_no_:0
                    ,fnd_reduced_stateno
                    ,la_set_no
                    );
                fsm->ow_file_ << fsm->big_buf_ << endl;
	   }
	}
    fsm->ow_file_ << "}" << endl;
  }
	KCHARP w_end =	"@** Index.";
 	fsm->ow_file_ << w_end << endl;
 	fsm->ow_file_.close();
  ***
  }
}

Rgrammar_phrase  (){
  -> "grammar-phrase" 
}
 
Rks_epi  
/@
Due to technical problems with the |convertMPtoPDF| macro,
the epsilon subrule is drawn with a dot.
@/
(){
  -> Rks 
  ->  
}

Rks  (){
  -> Rks Rk 
  -> Rk 
}

Rk  (){
  -> "cweb-marker" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
	WRT_CWEB_MARKER(&fsm->ow_file_,sf->p1__->ast());   
  ***  
   } 
}

Rfsm_phrase  (){
  -> "fsm-phrase" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    fsm->MPOST_CWEB_wrt_fsm(fsm,sf->p1__);
  ***
  }
}
       
Rparallel_phrase  (){
  -> "parallel-parser-phrase" 
  -> |.|
}
      
RT_enum_phrase  (){
  -> "T-enum-phrase" 
}
      
Rerr_phrase  (){
  -> "error-symbols-phrase" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    fsm->MPOST_CWEB_wrt_Err(fsm,sf->p1__);
  ***
  } 
}
          
Rrc_phrase  (){
  -> "rc-phrase" 
}

Rlr1_k_phrase  (){
  -> "lr1-k-phrase" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    fsm->MPOST_CWEB_wrt_Lrk(fsm,sf->p1__);
  ***
  } 
}

Rterms_phrase  (){
  -> "terminals-phrase" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    fsm->MPOST_CWEB_wrt_T(fsm,sf->p1__);
  ***
  } 
}

Rrule_phrase  
/@
Define the number of rules for |mpost|.
@/
(){
  -> "rules-phrase" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
	fsm->no_of_rules_ = sf->p1__->rules_alphabet()->size(); 
 	KCHARP mp_no_of_rules = "no_of_rules := %i;";
 	sprintf(fsm->big_buf_,mp_no_of_rules,fsm->no_of_rules_);
 	fsm->omp_file_ << fsm->big_buf_ << endl;
  ***
  }
}

Rrules  (){
  -> Rks_epi Rrule
  -> Rrules Rks_epi Rrule
}

Rrule  (){
  -> Rrule_def Rsubrules
}

Rrule_def  
/@
Define rule for |mpost| and |cweb|.
To help the grammar writer, walk the rule's subrules to generate 
a |cweb| cross-reference per subrule element.
Originally this was done at the subrule level
to be specific to the subrule entry instead of at the overall
rule level.
This meant that each subrule had to be redrawn with a |cweb|
directive
even when there was no syntax directed code present.
Just ugly is my take so i follow Occam's dictum.
@/
(){
  -> "rule-def" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    fsm->rule_def_ = sf->p1__;
    fsm->rules_for_fs_prt_.push_back(fsm->rule_def_);
 	KCHARP rname = fsm->rule_def_->rule_name()->c_str();
    ++fsm->w_fig_no_;
    ++fsm->rule_no_;
    fsm->subrule_no_ = 0;

	KCHARP mp_rule_names = 	
		"rule_names[%i].literal := \"%s\";"
		"rule_names[%i].vname := \"%s\";";
 	  
 	sprintf(fsm->big_buf_,mp_rule_names,fsm->rule_no_,rname,fsm->rule_no_,rname);
 	fsm->omp_file_ << fsm->big_buf_ << endl;

 	T_subrules_phrase* sr_ph = fsm->rule_def_->subrules();
 	fsm->no_of_subrules_ = sr_ph->no_subrules();
	KCHARP mp_rule_s_no_rhs = "rule_s_no_rhs[%i] := %i;";
 	sprintf(fsm->big_buf_,mp_rule_s_no_rhs
		,fsm->rule_no_,fsm->no_of_subrules_);
 	fsm->omp_file_ << fsm->big_buf_ << endl;
    fsm->no_subrules_per_rule_.push_back(fsm->no_of_subrules_);//[rule \#]
    
    KCHARP rule_cweb = 
		"@*2 |@!%s| rule.\\fbreak\n";
 	sprintf(fsm->big_buf_,rule_cweb,rname);
 	fsm->ow_file_ << fsm->big_buf_;
    if(fsm->rule_def_->cweb_marker() != 0){
		WRT_CWEB_MARKER(&fsm->ow_file_,fsm->rule_def_->cweb_marker());   
    }
    KCHARP rule_cweb_diagram = 
		"\\fbreak\n"
		"\\convertMPtoPDF{%s.%i}{1}{1}\n"; 
 	sprintf(fsm->big_buf_,rule_cweb_diagram
 			,fsm->grammar_filename_prefix_.c_str()
 			,fsm->w_fig_no_);
 	fsm->ow_file_ << fsm->big_buf_;
    std::vector<T_subrule_def*>* srules = sr_ph->subrules();
    std::vector<T_subrule_def*>::iterator i =  srules->begin();
    std::vector<T_subrule_def*>::iterator ie =  srules->end();
    for(;i!=ie;++i){
      T_subrule_def* srd = *i;
      fsm->MPOST_CWEB_gen_sr_elem_xrefs(fsm,srd->subrule_s_tree());
    }
    fsm->MPOST_CWEB_wrt_rule_s_lhs_sdc(fsm,fsm->rule_def_);
   
  ***
  }
}

Rsubrules  (){
  -> Rsubrule
  -> Rsubrules Rsubrule
}

Rsubrule  (){
  -> Rsubrule_def Relements
}

Rsubrule_def  (){
  -> "subrule-def" {
 /@
  The |eosubrule| T has no meaning in the drawn subrule.
  It is just a specific end-of-subrule indicator.
  So it is removed from the rhs element count of the subrule 
  for |mpost| processing so that it does not get drawn.
  The balance of the logic relates to |cweb|'s document:
  comments about the subrule and 
  the appropriate |mpost| rendering.
  
  One subtlety is the forced drawing of a single
  subrule that also has a rule's lhs syntax directed code.
  Normally i save the redrawing of the subrule as the
  forest and the tree is the same. But
  having the rule syntax code requires to have
  the subrule directive. Without it there are 2 
  code directives back-to-back that honks.
@/
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    ++fsm->w_fig_no_;
    ++fsm->subrule_no_;
    fsm->elem_no_ = 0;
    fsm->subrule_def_ = sf->p1__;
    int no_elements = fsm->subrule_def_->no_of_elems()-1;
    if(no_elements == 0) no_elements = 1;// epsilon subrule
	KCHARP mp_rule_s_subrule_no_elems = "rule_s_subrule_no_elems[%i][%i]:= %i;";
    sprintf(fsm->big_buf_,mp_rule_s_subrule_no_elems
 			,fsm->rule_no_,fsm->subrule_no_,no_elements);
 	fsm->omp_file_ << fsm->big_buf_ << endl;

	if(fsm->MPOST_CWEB_should_subrule_be_printed(fsm,fsm->subrule_def_) 
		== false) return;

 	KCHARP rname = fsm->rule_def_->rule_name()->c_str();

    KCHARP subrule_cweb = 
		"@*3 |%s|'s subrule %i.\\fbreak\n";
	if((fsm->rule_def_->rule_lhs() != 0) 
	    ||
	   (fsm->no_of_subrules_ > 1)){
 		  sprintf(fsm->big_buf_,subrule_cweb,rname,fsm->subrule_no_);
 		  fsm->ow_file_ << fsm->big_buf_;
 	}
    if(fsm->subrule_def_->cweb_marker() != 0){
		WRT_CWEB_MARKER(&fsm->ow_file_,fsm->subrule_def_->cweb_marker());   
		cout << "subrule's cweb marker for rule: " << rname << endl;
    }else{
		cout << "no subrule's cweb marker for rule: " << rname << endl;
		}

    KCHARP subrule_cweb_diagram = 
		"\\fbreak"
		"\\convertMPtoPDF{%s.%i}{1}{1}\n"; 
	if((fsm->rule_def_->rule_lhs() != 0) 
	    ||
	   (fsm->no_of_subrules_ > 1)){
 	  sprintf(fsm->big_buf_,subrule_cweb_diagram
 			,fsm->grammar_filename_prefix_.c_str()
 			,fsm->w_fig_no_);
 	  fsm->ow_file_ << fsm->big_buf_;
 	}

    fsm->MPOST_CWEB_woutput_sr_sdcode(fsm,fsm->subrule_def_);
  ***
  }
}

Relements  (){
  -> Relement
  -> Relements Relement
}

Relement 
/@
Use of \invisibleshift to make grammar lr(1).
@/ 
(){
  -> "refered-T" |.| {
/@
Why is there a test for ${\backslash}$" within the T?
Glad u asked as |mpost| has problems dealing with
this legitimate escape sequence and the double 
quote sequence within the c string.
So this was my 1st attempt to silence the honk.
Unfortunately the char(34) |mpost| operator does 
not convert the 34 into
the " character.
For the moment i will leave this as is. U will see other attempts
like this throughout the grammar.
@/

  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    ++fsm->elem_no_;
    std::string mp_obj_type("Circle_solid");
    std::string mp_elem_name(sf->p1__->t_in_stbl()->t_def()->t_name()->c_str());
    string::size_type x = mp_elem_name.find("\\\"");
    if(x != string::npos){
      mp_elem_name.clear();
      mp_elem_name+= "char(34)";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
  ***
  }
  -> "refered-rule" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    ++fsm->elem_no_;
    std::string mp_obj_type("Box_solid");
    std::string mp_elem_name(sf->p1__->Rule_in_stbl()->
	r_def()->rule_name()->c_str());
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
  ***
  }
  -> "eosubrule" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    ++fsm->elem_no_;
    if(fsm->elem_no_ == 1){// epsilon
       std::string mp_obj_type("Circle_solid");
       std::string mp_elem_name(" ");
       fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
     }
  ***
  }
  -> "refered-T" "refered-T" "called thread eosubrule" {
 /@
 Why is there a re-aligning on the number of 
 |rule_s_subrule_no_elems|?
 Two reasons: Due to the tree walk being
 top down, initially the number of items are determined
 by the |subrule-def| T before processing the actual subrule sentence.
 Due to the calling of threads, this number gets shrunk as i
 combine the 3 parts of the called thread into 1 item.
 Though it looks like a mistake in the emitted |mpost| code,
 it's a slight inefficiency to manage a nice picture.\fbreak
 \fbreak
 This comment hold for the other version of thread call below. 
 @/
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
	KCHARP re_align_nos = "rule_s_subrule_no_elems[%i][%i]:= %i;";
    sprintf(fsm->big_buf_,re_align_nos
 			,fsm->rule_no_,fsm->subrule_no_,3);
 	fsm->omp_file_ << fsm->big_buf_ << "% re-align cnt" << endl;
    ++fsm->elem_no_;
    std::string mp_obj_type("Circle_dotted");
    std::string mp_elem_name;
      if(sf->p1__->its_t_def()->enum_id() == T_Enum::T_LR1_parallel_operator_){    
        mp_elem_name += "|||";
      }else{
        mp_elem_name += "|t|";
      }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
    
    ++fsm->elem_no_;
    mp_obj_type.clear();
    mp_obj_type += "Circle_dotted";
    mp_elem_name.clear();
    mp_elem_name += sf->p2__->t_in_stbl()->t_def()->t_name()->c_str();
    string::size_type x = mp_elem_name.find("\\\"");
    if(x != string::npos){
      mp_elem_name.erase();
      mp_elem_name+= "char(34)";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);

    ++fsm->elem_no_;
    mp_elem_name.clear();
    mp_obj_type.clear();
    mp_obj_type += "Box_dotted"; 
	mp_elem_name += sf->p3__->ns()->identifier()->c_str();
	mp_elem_name += "::";
	mp_elem_name += sf->p3__->called_thread_name()->identifier()->c_str();
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
  ***
  }
  -> "refered-T" "refered-T" "null call thread eosubrule" {
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
	KCHARP re_align_nos = "rule_s_subrule_no_elems[%i][%i]:= %i;";
    sprintf(fsm->big_buf_,re_align_nos,fsm->rule_no_,fsm->subrule_no_,3);
 	fsm->omp_file_ << fsm->big_buf_ << "% re-align cnt" << endl;

    ++fsm->elem_no_;
    std::string mp_obj_type("Circle_dotted");
    std::string mp_elem_name;
      if(sf->p1__->its_t_def()->enum_id() == T_Enum::T_LR1_parallel_operator_){    
        mp_elem_name += "|||";
    }else{
        mp_elem_name += "|t|";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
    mp_elem_name.clear();
    
    ++fsm->elem_no_;
    mp_elem_name += sf->p2__->t_in_stbl()->t_def()->t_name()->c_str();
    string::size_type x = mp_elem_name.find("\\\"");
    if(x != string::npos){
      mp_elem_name.clear();
      mp_elem_name+= "char(34)";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);

    ++fsm->elem_no_;
    mp_elem_name.clear();
    mp_obj_type.clear();
    mp_obj_type += "Box_dotted"; 
	mp_elem_name += "NULL";
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
  ***
  }
  -> "refered-T" "refered-T" |.|{
  op
    Cmpost_output* fsm = (Cmpost_output*)rule_info__.parser__->fsm_tbl__;
    ++fsm->elem_no_;
    std::string mp_obj_type("Circle_solid");
    std::string mp_elem_name(sf->p1__->t_in_stbl()->t_def()->t_name()->c_str());
    string::size_type x = mp_elem_name.find("\\\"");
    if(x != string::npos){
      mp_elem_name.erase();
      mp_elem_name+= "char(34)";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);

    ++fsm->elem_no_;
    mp_obj_type.clear();
    mp_obj_type += "Circle_solid";
    mp_elem_name.clear();
    mp_elem_name += sf->p2__->t_in_stbl()->t_def()->t_name()->c_str();
    x = mp_elem_name.find("\\\"");
    if(x != string::npos){
      mp_elem_name.erase();
      mp_elem_name+= "char(34)";
    }
    fsm->MPOST_CWEB_wrt_mp_rhs_elem(fsm,mp_elem_name,mp_obj_type);
  ***
  }
}

}// end of rules
