@q file: o2externs.w@> @q% Copyright Dave Bone 1998 - 2015@> @q% /*@> @q% This Source Code Form is subject to the terms of the Mozilla Public@> @q% License, v. 2.0. If a copy of the MPL was not distributed with this@> @q% file, You can obtain one at http://mozilla.org/MPL/2.0/.@> @q% */@> @q% This file is part of YACC02 compiler/compiler project.@> \input "eplain" \input "supp-pdf" \input "/usr/local/yacco2/diagrams/o2mac.tex" %\tracingall=1 %\tracingall=1 %\tracingmacros=1 %\tracingcommands=2 @i "/usr/local/yacco2/license.w" @** Summary of \O2 External parse routines.\fbreak These are the various procedures that parse \Yacco2's grammar language and emit the grammar's c++ code and tex document with mpost generated diagrams. Each language construct has its appropriate external procedure that houses the monolithic grammar to start the parse. There is no namespace used to contain these routines as I felt that this was overkill. As this is a closed system, their grammars are not universal and cannot be re-cycled for others. Their only outside value are in teaching examples on ``how to skin a cat'' or is it ``how to parse a lion?''... External routines ratatouille:\fbreak \ptindent{|o2externs.w| - cweb generator file} \ptindent{|o2_externs.h| - header file} \ptindent{|o2_externs.cpp| - implementation} Dependency files from other Yacco2 sub-systems:\fbreak \ptindent{|yacco2.h| - basic definitions used by Yacco2} \ptindent{|yacco2_T_enumeration.h| - terminal enumeration for Yacco2's terminal grammar alphabet} \ptindent{|yacco2_err_symbols.h| - error terminal definitions from Yacco2's grammar alphabet} \ptindent{|yacco2_characters.h| - raw character definitions from Yacco2's grammar alphabet} \ptindent{|yacco2_k_symbols.h| - constant meta terminal defs from Yacco2's grammar alphabet} \ptindent{|yacco2_terminals.h| - regular terminal definitions from Yacco2's grammar alphabet} \ptindent{|*.h| - assorted grammar definitions for Yacco2's parsing} \ptindent{|yacco2_stbl.h| - symbol table defnitions} External procedures and other globals:\fbreak \ptindent{|YACCO2_PARSE_CMD_LINE|} \ptindent{|PROCESS_INCLUDE_FILE|} \ptindent{|PROCESS_KEYWORD_FOR_SYNTAX_CODE|} \ptindent{|O2_xxx| phases --- terminals per distinct parse phrase} \ptindent{|PRINT_RULES_TREE_STRUCTURE|} \ptindent{|WRT_CWEB_MARKER|} \ptindent{|GEN_FS_OF_RULE|} \ptindent{|PRT_RULE_S_FIRST_SET|} \ptindent{|OP_GRAMMAR_HEADER|} \ptindent{|OP_ERRORS_HEADER|} \ptindent{|OP_USER_T_HEADER|} \ptindent{|DATE_AND_TIME|} \ptindent{|MAX_USE_CNT_RxSkeletion| --- rule recyling} \ptindent{|XLATE_SYMBOLS_FOR_cweave|} Internal procedures:\fbreak \ptindent{|process_fsm_phrase|} \ptindent{|process_parallel_parser_phrase|} \ptindent{|process_T_enum_phrase|} \ptindent{|process_error_symbols_phrase|} \ptindent{|process_rc_phrase|} \ptindent{|process_lr1_k_phrase|} \ptindent{|process_terminals_phrase|} \ptindent{|process_rules_phrase|} \ptindent{|Print_dump_state|} @** Global definitions, External parse routines for Yacco2.\fbreak Why |CWEB_MARKER| external? It holds the |T_cweb_marker| tree containing |cweb| comments. Why? It is a holding pointer that allows |cweb| comments to be processed before the appropriate parse phrases like |process_xxx_phrase| that are triggered by |PROCESS_KEYWORD_FOR_SYNTAX_CODE|. \def\ptindent#1{\line{\hskip.5in{#1}\hfill}} \def\fbreak{\hfill\break} \def\wildcard{$\vert$+$\vert$} \def\parallelop{$\vert\vert\vert$} @ Create header file. @(o2_externs.h@>= #ifndef o2_externs_ #define o2_externs_ 1 @h @; #endif @*2 Files for header. @= #include "globals.h" #include "o2_types.h" #include "o2_lcl_opts.h" #include "o2_lcl_opt.h" #include "pass3.h" #include "o2_err_hdlr.h" #include "fsm_phrase.h" #include "parallel_parser_phrase.h" #include "T_enum_phrase.h" #include "err_symbols_ph.h" #include "rc_phrase.h" #include "lr1_k_phrase.h" #include "terminals_phrase.h" #include "rules_phrase.h" #include "yacco2_stbl.h" #include "enumerate_T_alphabet.h" #include "mpost_output.h" #include "prt_xrefs_docs.h" #include "cweb_put_k_into_ph.h" extern CYCLIC_USE_TBL_type CYCLIC_USE_TABLE; extern STBL_T_ITEMS_type STBL_T_ITEMS; extern int NO_LR1_STATES; extern T_fsm_phrase*O2_FSM_PHASE; extern T_parallel_parser_phrase*O2_PP_PHASE; extern T_enum_phrase*O2_T_ENUM_PHASE; extern T_lr1_k_phrase*O2_LRK_PHASE; extern T_rc_phrase*O2_RC_PHASE; extern T_error_symbols_phrase*O2_ERROR_PHASE; extern T_terminals_phrase*O2_T_PHASE; extern T_rules_phrase*O2_RULES_PHASE; extern STATES_type LR1_STATES; extern RULE_ENO START_OF_RULES_ENUM; extern STBL_T_ITEMS_type STBL_T_ITEMS; extern RULE_ENO START_OF_RULES_ENUM; extern yacco2::AST* GRAMMAR_TREE; extern yacco2::AST* CWEB_MARKER; extern void WRT_CWEB_MARKER(std::ofstream* Wfile,yacco2::AST* Cweb_marker); extern void LOAD_YACCO2_KEYWORDS_INTO_STBL(); extern void GET_CMD_LINE(int argc, char* argv[] ,const char* File,yacco2::TOKEN_GAGGLE& Errors); extern void DUMP_ERROR_QUEUE(yacco2::TOKEN_GAGGLE& Errors); extern void PRINT_RULES_TREE_STRUCTURE(AST* Node); extern const char* DATE_AND_TIME(); extern void YACCO2_PARSE_CMD_LINE@/ (yacco2::CHAR& T_sw@/ ,yacco2::CHAR& ERR_sw,yacco2::CHAR& PRT_sw@/ ,std::string& Grammar_to_compile@/ ,yacco2::TOKEN_GAGGLE& Error_queue); extern bool PROCESS_INCLUDE_FILE@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_file_inclusion& File_include@/ ,yacco2::token_container_type& T2); extern bool PROCESS_KEYWORD_FOR_SYNTAX_CODE@/ (yacco2::Parser& Parser@/ ,yacco2::CAbs_lr1_sym* Keyword@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos); extern void BUILD_GRAMMAR_TREE(yacco2::AST& Item); extern void PRINT_GRAMMAR_TREE(AST* Node); extern void GEN_FS_OF_RULE(NS_yacco2_terminals::rule_def* Rule_def); extern void GEN_CALLED_THREADS_FS_OF_RULE@/ (NS_yacco2_terminals::rule_def* Start_rule); extern int MAX_USE_CNT_RxR@/ (NS_yacco2_terminals::rule_def* Rule_use ,NS_yacco2_terminals::rule_def* Against_rule); extern void XLATE_SYMBOLS_FOR_cweave(const char* Sym_to_xlate,char* Xlated_sym); extern void PRT_RULE_S_FIRST_SET(NS_yacco2_terminals::rule_def* Rule_def); extern void OP_GRAMMAR_HEADER(TOKEN_GAGGLE& Error_queue); extern void OP_GRAMMAR_CPP(TOKEN_GAGGLE& Error_queue); extern void OP_GRAMMAR_SYM(TOKEN_GAGGLE& Error_queue); extern void OP_GRAMMAR_TBL(TOKEN_GAGGLE& Error_queue); extern void OP_ENUMERATION_HEADER(TOKEN_GAGGLE& Error_queue); extern void OP_T_Alphabet(TOKEN_GAGGLE& Error_queue); extern void OP_ERRORS_HEADER(TOKEN_GAGGLE& Error_queue); extern void OP_ERRORS_CPP(TOKEN_GAGGLE& Error_queue); extern void OP_USER_T_HEADER(TOKEN_GAGGLE& Error_queue); extern void OP_USER_T_CPP(TOKEN_GAGGLE& Error_queue); extern void OP_FSC_FILE(TOKEN_GAGGLE& Error_queue); extern void Print_dump_state(state* State); @ Include Header file. @= #include "o2_externs.h" @ Yacco2 external routines blueprint. Output of the code. @(o2_externs.cpp@>= @; @; @ Accrue source for emit. @= RULES_HAVING_AR_type RULES_HAVING_AR; COMMON_LA_SETS_type COMMON_LA_SETS; T_fsm_phrase*O2_FSM_PHASE(0); T_parallel_parser_phrase*O2_PP_PHASE(0); T_enum_phrase*O2_T_ENUM_PHASE(0); T_lr1_k_phrase*O2_LRK_PHASE(0); T_rc_phrase*O2_RC_PHASE(0); T_error_symbols_phrase*O2_ERROR_PHASE(0); T_terminals_phrase*O2_T_PHASE(0); T_rules_phrase*O2_RULES_PHASE(0); yacco2::AST* CWEB_MARKER(0); yacco2::AST* GRAMMAR_TREE(0); RULE_ENO START_OF_RULES_ENUM(-1); extern STBL_T_ITEMS_type STBL_T_ITEMS; @** Local Yacco2 routines.\fbreak @ Prescan literal names within |mpost|. Due to a bug in the ``convertMPtoPDF'' macro, spaces are edited. @+= void prescan_mpname_for_cweb(std::string& In,std::string& Out){ int len = In.length(); for(int x=0;x+= extern void Print_dump_state(state* State){ @; } @ @= const char* literal_name; yacco2::lrclog << std::endl; yacco2::lrclog << "->State: "; state* cur_state = State; literal_name = cur_state->entry_symbol_literal(); yacco2::lrclog << cur_state->state_no_ << " Entry: " << cur_state->vectored_into_by_elem_ << " Symbol: " << literal_name; if(cur_state->closure_state_birthing_it_!=0){ yacco2::lrclog << " Birthing closure state: " << cur_state->closure_state_birthing_it_->state_no_; } yacco2::lrclog<< std::endl; yacco2::lrclog << " Follow set:" << std::endl; S_FOLLOW_SETS_ITER_type sfi = cur_state->state_s_follow_set_map_.begin(); S_FOLLOW_SETS_ITER_type sfie = cur_state->state_s_follow_set_map_.end(); for(;sfi!=sfie;++sfi){ follow_element* fe = sfi->second; @=rule_def* rd = (rule_def*)AST::content(*fe->rule_def_t_);@>@/ yacco2::lrclog << " Rule no: " << fe->rule_no_ << " Name: " << rd->rule_name()->c_str() << std::endl; FOLLOW_SETS_ITER_type i = fe->follow_set_.begin(); FOLLOW_SETS_ITER_type ie = fe->follow_set_.end(); if(i!=ie){ yacco2::lrclog<<" follow set:"< 15) { yacco2::lrclog << endl; yacco2::lrclog << "\t\t"; nos = 1; } yacco2::lrclog << tit->t_def()->t_name()->c_str() << " "; } TRANSITIONS_ITER_type j = fe->transitions_.begin(); TRANSITIONS_ITER_type je = fe->transitions_.end(); if(j!=je){ yacco2::lrclog<<" transitions"<rule_def_t_);@>@/ yacco2::lrclog << " S" << tfe->its_state_->state_no_ << "x" << rd->rule_name()->c_str() << endl; } MERGES_ITER_type k = fe->merges_.begin(); MERGES_ITER_type ke = fe->merges_.end(); if(k!=ke){ yacco2::lrclog<<" merges"<state_no_ << " " ; } yacco2::lrclog << endl; } yacco2::lrclog << " Vectors:" << std::endl; S_VECTORS_ITER_type svi = cur_state->state_s_vector_.begin(); S_VECTORS_ITER_type svie = cur_state->state_s_vector_.end(); for(;svi!=svie;++svi){ yacco2::lrclog << " Symbol no: " << svi->first; int first_time(0); S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); S_VECTOR_ELEMS_ITER_type selie = svi->second.end(); for(;seli!=selie;++seli){ state_element* se = *seli; if(first_time == 0){ first_time = 1; CAbs_lr1_sym* sym = AST::content(*se->sr_element_); yacco2::lrclog << " Symbol: "; switch(sym->enumerated_id__){ case T_Enum::T_refered_rule_:{ @; rule_def* rd = rr->its_rule_def(); yacco2::lrclog << rd->rule_name()->c_str() << endl; break; } case T_Enum::T_refered_T_:{ @; T_terminal_def* td = rt->its_t_def(); yacco2::lrclog << td->t_name()->c_str() << endl; break; } default:{ yacco2::lrclog << sym->id() << endl; break; } } } @; } } if(cur_state->state_s_conflict_state_list_.empty() != true){ yacco2::lrclog << " ===>Conflict states:" << std::endl; S_CONFLICT_STATES_ITER_type csi = cur_state->state_s_conflict_state_list_.begin(); S_CONFLICT_STATES_ITER_type csie = cur_state->state_s_conflict_state_list_.end(); for(;csi!=csie;++csi){ state* cstate = *csi; yacco2::lrclog << " State no: " << cstate->state_no_ << endl; } } yacco2::lrclog << "End of state" << std::endl; @ @= CAbs_lr1_sym* sym = AST::content(*se->sr_element_); Voc_ENO id = sym->enumerated_id__; @; @ @= switch(id){ case T_Enum::T_refered_rule_:{ @; yacco2::lrclog << " RxSRxPos: " << rr->grammar_s_enumerate()->c_str(); rule_def* rd = rr->its_rule_def(); yacco2::lrclog << " " << rd->rule_name()->c_str(); break; } case T_Enum::T_T_eosubrule_:{ @; if(se->la_set_!=0){ LA_SET_ITER_type i= se->la_set_->begin(); LA_SET_ITER_type ie= se->la_set_->end(); int nos(1); if(i != ie){ yacco2::lrclog << " reduce set #: "<< se->common_la_set_idx_<< endl; yacco2::lrclog << " "; for(;i!=ie;++i){ T_in_stbl*tit= *i; ++nos; if(nos> 15){ yacco2::lrclog<t_def()->t_name()->c_str()<<" "; } yacco2::lrclog<grammar_s_enumerate()->c_str() ; rule_def* rd = eos->its_rule_def(); yacco2::lrclog << " Eos of " << rd->rule_name()->c_str(); break; } case T_Enum::T_T_null_call_thread_eosubrule_:{ @; if(se->la_set_!=0){ LA_SET_ITER_type i= se->la_set_->begin(); LA_SET_ITER_type ie= se->la_set_->end(); int nos(1); if(i != ie){ yacco2::lrclog << " reduce set #: "<< se->common_la_set_idx_<< endl; yacco2::lrclog << " "; for(;i!=ie;++i){ T_in_stbl*tit= *i; ++nos; if(nos> 15){ yacco2::lrclog<t_def()->t_name()->c_str()<<" "; } yacco2::lrclog<grammar_s_enumerate()->c_str() ; rule_def* rd = eos->its_rule_def(); yacco2::lrclog << " Eos of " << rd->rule_name()->c_str(); break; } case T_Enum::T_T_called_thread_eosubrule_:{ @; if(se->la_set_!=0){ LA_SET_ITER_type i= se->la_set_->begin(); LA_SET_ITER_type ie= se->la_set_->end(); int nos(1); if(i != ie){ yacco2::lrclog << " reduce set #: "<< se->common_la_set_idx_<< endl; yacco2::lrclog << " "; for(;i!=ie;++i){ T_in_stbl*tit= *i; ++nos; if(nos> 15){ yacco2::lrclog<t_def()->t_name()->c_str()<<" "; } yacco2::lrclog<grammar_s_enumerate()->c_str() ; rule_def* rd = eos->its_rule_def(); yacco2::lrclog << " Eos of " << rd->rule_name()->c_str(); break; } case T_Enum::T_refered_T_:{ @; yacco2::lrclog << " RxSRxPos: " << rt->grammar_s_enumerate()->c_str(); T_terminal_def* td = rt->its_t_def(); yacco2::lrclog << " " << td->t_name()->c_str(); break; } } yacco2::lrclog << " Closured S" << se->closure_state_->state_no_; yacco2::lrclog << " CS-gening-it S" << se->closured_state_gening_it_->state_no_; if(se->closure_state_->state_no_ != se->closured_state_gening_it_->state_no_){ yacco2::lrclog << " due to rt bnd " ; } if(se->goto_state_ != 0){ yacco2::lrclog << " goto S" << se->goto_state_->state_no_; } if(se->reduced_state_ != 0){ yacco2::lrclog << " reduced S" << se->reduced_state_->state_no_; yacco2::lrclog << std::endl; }else{ yacco2::lrclog << " reduced S" << "????"; yacco2::lrclog << std::endl; } @ @= @=refered_rule* rr = (refered_rule*)AST::content(*se->sr_element_);@>@/ @ @= @=refered_rule* rr = (refered_rule*)sym;@>@/ @ @= @=refered_T* rt = (refered_T*)sym;@>@/ @ @= @=T_eosubrule* eos = (T_eosubrule*)sym;@>@/ @ @= @=T_null_call_thread_eosubrule* eos = (T_null_call_thread_eosubrule*)sym;@>@/ @ @= @=T_called_thread_eosubrule* eos = (T_called_thread_eosubrule*)sym;@>@/ @*2 |process_fsm_phrase|.\fbreak This process demonstrates quasi recursive descent and bottom-up grammar parsing. There is no recursion taking place: just a single call to get things going. The procedure is a packaging agent that houses a monolithic grammar |Cfsm_phrase| which then uses parallel parsing threads to do its deeds. The grammars parse a stream of characters which get turned into the appropriate tokens. This is an inbetween style of parsing of the lexical and syntax phrases interweaving together. This is due to the c++ syntax directed code being just a stream of characters where a lookahead in the character stream for `***' is done to close the code directive. Of course, this code directive is parsed in parallel for literal and comment tokens which can subsume this. The reason for this skip across the characters to end the c++ code approach was laziness! I did not want to develop a c++ suite of grammars to deal with the syntax directed code. The risk in this current approach is to misprogram the end-of-code indicator `***'. This could lead to grammar stream overrun (end-of-file reached) while thinking that one is still in the syntax directed code stream. It also passes the buck to the c++ compiler to deal with any c++ syntax directed code errors. At least this run-away type error is guarded against in the grammars. The other potential problem is the perverse pointer to a pointer to a pointer buried in the syntax directed code: ``***''. I thought of this situation and how to resolve it by use of a different character set: for example 3 pound signs. I nixed it as I felt it ulgy in aesthetics and as a highlighter of code boundaries. {\bf So heed the warnin}. The other interesting part to this family of grammars is in the use of the wild card facility to trap errors and how the subordinate grammars issue errors as normal tokens. This allows for a cleaner way to deal with error handling within a grammar's subrule phrase and associated syntax directed code. This is achieved by writing explicit production subrules using these error terminals or to program the wild card catch all facility. Take a look in the grammars at how \wildcard{} is used. The last note is how the newly generated tokens are dealt with when an error occurs. This is rather easy, the holding token |T_fsm_phrase| is deleted. It contains all the important tokens created in its parse. Hence when there is a ruptured grammar sequence, the accumulated tokens are deleted by |T_fsm_phrase| destructor. Throw away tokens like white space and comments that are part of the fsm phrase are auto deleted when popped from the parse stack. Comments and white space within the syntax directed code are kept. This is why the output token container's contents are not deleted. There is no |yacco2::Delete_tokens(...);| statement in this procedure. The holding token is deposited in the passed |T_fsm| parameter. |O2_FSM_PHASE| holds the |T_fsm_phrase| terminal. Please see its family of grammars:\fbreak \ptindent{|fsm_phrase|.lex - monolithic grammar} \ptindent{|fsm_phrase_th|.lex - parse fsm phrase} \ptindent{|fsm_attributes|.lex - fsm keywords recognizer} \ptindent{|fsm_class_directives|.lex - fsm syntax code directives} \ptindent{|fsm_class_phrase_th|.lex - parse fsm-class phrase containing c++ code snippets} @^\PB{fsm\_phrase.lex}@> @^\PB{fsm\_phrase\_th.lex}@> @^\PB{fsm\_attributes.lex}@> @^\PB{fsm\_class\_directives.lex}@> @^\PB{fsm\_class\_phrase\_th.lex}@> @+= bool process_fsm_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_fsm& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); using namespace NS_fsm_phrase; Cfsm_phrase fsm; Parser p1(fsm,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_fsm_phrase* t = (T_fsm_phrase*)sym@>; Phrase_to_parse.fsm_phrase(t); if(O2_FSM_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_fsm_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_FSM_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in fsm-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 Output macros and banner. @+= void output_cweb_macros_and_banner( NS_mpost_output::Cmpost_output* Fsm ,std::ofstream& Ofile ,yacco2::KCHARP Banner ,const char* Date ,string* Grammar_name ,string* Name_space ,int No_t){ using namespace NS_mpost_output; KCHARP macros = "\\input%s \"supp-pdf\"\n"@/ "\\input \"/usr/local/yacco2/diagrams/o2mac.tex\"\n"; int x = sprintf(Fsm->big_buf_,macros," "); Ofile.write(Fsm->big_buf_,x); KCHARP banner = "\\DOCtitle{%s}{%s}%\n" "{%s}{%i}\n"; char fname[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Grammar_name->c_str(),fname); char ns_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Name_space->c_str(),ns_name); x = sprintf(Fsm->big_buf_,banner ,Banner ,fname,ns_name,No_t); Ofile.write(Fsm->big_buf_,x); } @*2 |process_parallel_parser_phrase|.\fbreak This parses the parallel component of a grammar. It supplies the threading components and lookahead expression for a threaded grammar. One thing of note, the lookahead expression is held as a character stream which gets parsed after the complete grammar has been recognized. The reason for this is in the lookahead expression's potential use of productions (rules). Rules are packaging agents to haul in terminals as elements for the first set. As the production name used has not yet been defined, one must wait until the complete production section has been parsed before obtaining its FIRST SET of terminals for the thread's lookahead expression. Only then can the lookahead character stream be parsed in terms of ``rule-in-stbl'' and ``T-in-stbl'' tokens. From these tokens, the expression set will be calculated from their terminals. The production declaration is not restricted to use within the grammar's grammatical phrases though a check will be done with an appropriate warning if the production has not been used somewhere within the grammar or parallel parser lookahead expression. The lookahead expression uses addition and subtraction of terminal terms to arrive at the lookahead set. The |eolr| terminal has the meaning of ``use all terminals defined including self''. The advantage is two fold: its supplies all the terminals thus shrinking substantially the lookahead set's size --- only one terminal in its set, and two it eases the grammar writer's fingers in that the expression is much shorter to express. |O2_PP_PHASE| holds the |T_parallel_parser_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|parallel_parser_phrase|.lex - monolithic grammar} \ptindent{|parallel_parser_phrase_th|.lex - parse parallel parser construct} \ptindent{|parallel_parser_attributes|.lex - parallel parser keywords recognizer} @^\PB{parallel\_parser\_phrase.lex}@> @^\PB{parallel\_parser\_phrase\_th.lex}@> @^\PB{parallel\_parser\_attributes.lex}@> @+= bool process_parallel_parser_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_parallel_parser& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_parallel_parser_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Cparallel_parser_phrase pparser; Parser p1(pparser,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_parallel_parser_phrase* t = (T_parallel_parser_phrase*)sym@>; Phrase_to_parse.parallel_parser_phrase(t); if(O2_PP_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_pp_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_PP_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in parallel-parser-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_T_enum_phrase|.\fbreak Its use is in housing all the enumerated terminals from the generated alphabets and possible user included constant definitions that are needed outside the grammars. These span the spectrums of errors, raw characters, LR constants, and finally the pedestrian terminals. The enumeration is lr k starting at 0 $<$ raw characters $<$ error $<$ regular terminals. As raw chars and ``lr k'' terminals are constant, the other two alphabets grow outwards in the right direction as new members are defined. |O2_T_ENUM_PHASE| holds the |T_enum_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|T_enum_phrase|.lex - monolithic grammar} \ptindent{|T_enum_phrase_th|.lex - parse |T_enumeration| construct} \ptindent{|T_enum_attributes|.lex - |T_enumeration| keywords recognizer} @^\PB{T\_enum\_phrase.lex}@> @^\PB{T\_enum\_phrase\_th.lex}@> @^\PB{T\_enum\_attributes.lex}@> @+= bool process_T_enum_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_enumeration& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_T_enum_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); CT_enum_phrase enum_ph; Parser p1(enum_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_enum_phrase* t = (T_enum_phrase*)sym@>; Phrase_to_parse.enum_phrase(t); if(O2_T_ENUM_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_T_enum_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_T_ENUM_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in t-enum-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_error_symbols_phrase|.\fbreak This handles the error alphabet introduced to the grammar. It is normally placed into its own file and brought into the grammar by way of the grammar's include file facility. It was designed this way to segregate the terminal symbols both in definition and own namespace enclosure. The same holds for the other terminal types: raw characters, LR constants, and the regular terminals. |O2_ERROR_PHASE| holds the |T_error_symbols_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|err_symbols_ph|.lex - monolithic grammar} \ptindent{|err_symbols_ph_th|.lex - parse error symbols construct} \ptindent{|error_symbols_attributes|.lex - error symbols keywords recognizer} @^\PB{error\_symbols\_phrase.lex}@> @^\PB{error\_symbols\_phrase\_th.lex}@> @^\PB{error\_symbols\_attributes.lex}@> @+= bool process_error_symbols_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_error_symbols& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_err_symbols_ph; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Cerr_symbols_ph err_ph; Parser p1(err_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_error_symbols_phrase* t = (T_error_symbols_phrase*)sym@>; Phrase_to_parse.error_symbols_phrase(t); if(O2_ERROR_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_error_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_ERROR_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in error-symbols-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_rc_phrase|.\fbreak This handles the raw character terminals. This is the alphabet of raw character terminals introduced to the grammar. It is normally placed into its own file and brought into the grammar by way of the grammar's include file facility. It was designed this way to segregate the terminal symbols both in definition and own namespace enclosure. |O2_RC_PHASE| holds the |T_rc_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|rc_phrase|.lex - monolithic grammar} \ptindent{|rc_phrase_th|.lex - parse error symbols construct} \ptindent{|rc_attributes|.lex - raw character symbols keywords recognizer} @^\PB{rc\_symbols\_phrase.lex}@> @^\PB{rc\_phrase\_th.lex}@> @^\PB{rc\_attributes.lex}@> @+= bool process_rc_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_raw_characters& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_rc_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Crc_phrase rc_ph; Parser p1(rc_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_rc_phrase* t = (T_rc_phrase*)sym@>; Phrase_to_parse.rc_phrase(t); if(O2_RC_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_rc_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_RC_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in rc-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_lr1_k_phrase|.\fbreak This handles the |lr_k| of constants used throughout the grammars: for example, end-of-grammar, parallel operators and the like. It is normally placed into its own file and brought into the grammar by way of the grammar's include file facility. It was designed this way to segregate the terminal symbols both in definition and own namespace enclosure. |O2_LRK_PHASE| holds the |T_lr1_k_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|lr1_k_phrase|.lex - monolithic grammar} \ptindent{|lr1_k_phrase_th|.lex - parse lr1 k symbols construct} \ptindent{|lr1_k_attributes|.lex - lr1 k symbols keywords recognizer} @^\PB{lr1\_k\_phrase.lex}@> @^\PB{lr1\_k\_phrase\_th.lex}@> @^\PB{lr1\_k\_attributes.lex}@> @+= bool process_lr1_k_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_lr1_constant_symbols& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_lr1_k_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Clr1_k_phrase lr_ph; Parser p1(lr_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_lr1_k_phrase* t = (T_lr1_k_phrase*)sym@>; Phrase_to_parse.lr1_k_phrase(t); if(O2_LRK_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_lrk_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_LRK_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in lr1-k-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_terminals_phrase|.\fbreak This handles the regular terminals alphabet introduced to the grammar. It is normally placed into its own file and brought into the grammar by way of the grammar's include file facility. It was designed this way to segregate the terminal symbols both in definition and own namespace enclosure. This is the grammar writer's stable of terminals used by the grammars to process the specific language. |O2_T_PHASE| holds the |T_terminals_phrase| terminal. Its family of grammars are:\fbreak \ptindent{|terminals_phrase|.lex - monolithic grammar} \ptindent{|terminals_phrase_th|.lex - parse terminal symbols construct} \ptindent{|terminals_attributes|.lex - terminal symbols keywords recognizer} @^\PB{terminals\_phrase.lex}@> @^\PB{terminals\_phrase\_th.lex}@> @^\PB{terminals\_attributes.lex}@> @+= bool process_terminals_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_terminals& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_terminals_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Cterminals_phrase term_ph; Parser p1(term_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_terminals_phrase* t = (T_terminals_phrase*)sym@>; Phrase_to_parse.terminals_phrase(t); if(O2_T_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_T_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_T_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); return Success; } error_exit: lrclog << "error in terminals-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @*2 |process_rules_phrase|.\fbreak This parses the rule's alphabet of the grammar. In grammar parlance, this is the |productions| and |rules| rolled into one. The rule names are local to the grammar. They do not use global space allowing the grammar writer to assign meaningful rule names of reuse patterns expressed throughout each grammar. The `fsm' structure of the grammar has a namespace property that allows each grammar to be mixed in global space. |O2_RULES_PHASE| holds the |T_rules_phrase| terminal. Why the enumeration of the Terminal alphabet before processing rules? I wanted it as a discrete process rather than distributed throughout pieces of grammars, and to improve rule parsing, and i reference the parallel operator terminal ``\parallelop'' \ specifically for clarity in the sub-rule parsing. How so clarity? I felt dividing a grammar's productions into thread expressions or standard symbol strings made the subrule grammar more readable. It sharpened the distinction between the 2 types of grammars: monolithic and thread. Due to this, the \parallelop was explicitly programmed and its presence tested for using a fixed enumeration value rather than its literal string name. Its family of grammars are:\fbreak \ptindent{|rules_phrase|.lex - monolithic grammar} \ptindent{|rules_phrase_th|.lex - parse terminal symbols construct} \ptindent{|terminals_attributes|.lex - terminal symbols keywords recognizer} \ptindent{|rule_def_phrase|.lex - parse rule's definition construct} \ptindent{|rule_lhs_phrase|.lex - parse rule's lhs construct} \ptindent{|parallel_monitor_phrase|.lex - parse rule's arbitration construct} \ptindent{|subrules_phrase|.lex - parse subrule's definition construct} \ptindent{|subrule_def|.lex - parse a subrule's rhs expression} @^\PB{terminals\_phrase.lex}@> @^\PB{terminals\_phrase\_th.lex}@> @^\PB{terminals\_attributes.lex}@> @*4 Enumerate Terminal alphabet.\fbreak Each terminal phase contains its mapped symbols and ``create order'' list. The implied enumeration starts with the lrk symbols followed by raw characters, errors, and finally the terminals. The rank order is 0..n-1 where n is the total number of terminals in the alphabet. The ``create order'' list provides the symbol order. As i rant on relative zero from previous writings regarding vector access etc, why not here regarding the rank assignment from 0? Modulo arithmetic is used on the register's number of bits to calculate a specific element in the first set: a divisional way of doing things. To traverse the terminal symbols, |O2_xxx_PHASE| per phases. Each specific terminal phrase will be accessed and its symbols traversed by ``create order'' to assign their appropriate enumerate value. Each phase derives from the same base class :\fbreak \ptindent{0 - |O2_FSM_PHASE| : |T_fsm_phrase|*} \ptindent{1 - |O2_PP_PHASE| : |T_parallel_parser_phrase|*} \ptindent{2 - |O2_T_ENUM_PHASE| : |T_enum_phrase|*} \ptindent{3 - |O2_LRK_PHASE| : |T_lr1_k_phrase|*} \ptindent{4 - |O2_RC_PHASE| : |T_rc_phrase|*} \ptindent{5 - |O2_ERROR_PHASE| : |T_error_symbols_phrase|*} \ptindent{6 - |O2_T_PHASE| : |T_terminals_phrase|*} \ptindent{7 - |O2_RULES_PHASE| : |T_rules_phrase|*} Why use a grammar anyway when straight procedure calls would do the trick? Good question as the token stream is not even being consumed! This is my experiment in using grammars as a descriptor of software events --- not as efficient as in procedure calls but the intentions are explicit. Is not the procedure call explicit in intent? Yes it is within a no token consumption context. So, does this grammar approach look like an inefficient double indirection to achieve the same result. Yup. But i also use a sentence approach within the grammar of right-hand-side left-hand-side recognition to calculate the overall total of symbols enumerated. This processing model is a mainstay to pre-post event evaluation. Also, my grammars have automatic flow control tracing facility that comes out-of-the-box. Now consider when other events are surrounding a software process. Grammars are explicit finite state automata with ambiguity verification. I could have created behavioural terminals to explicitly express execution by the ``shift operation'' but this is overkill in this situation. For now this grammar has implicit behaviours using epsilon. It is interesting to reflect on how epsilon with no terminals can be used as an operation sequencer. Only the ``error'' container is passed to the parser. The i/o token containers are not supplied. @= lrclog << "Enumerate T alphabet" << endl; using namespace NS_enumerate_T_alphabet; Cenumerate_T_alphabet enum_syms_fsm; Parser enum_syms(enum_syms_fsm,0,0,0,Calling_parser.error_queue(),0,0); enum_syms.parse(); if(Calling_parser.error_queue()->empty() != true) return Failure; @*4 Enumerate Rule alphabet.\fbreak Enumeration starts after the errors vocabulary. The |START_OF_RULES_ENUM| global now registers the rules' enumerate boundary. To speed up the LR1 compatibilty check, this boundary becomes the cut off point in shift / reduce evaluations. |eosubrule| enumerate is excluded in the Terminals shift set of the state as it represents the ``end-of-subrule'' string condition: reduce.\fbreak \fbreak To speed up the pushdown automata, rules are recyclesd for re-use. To do this each rule needs a rule no relative to its create order starting from 1. @= lrclog << "Enumerate Rule alphabet" << endl; int Rules_enum = START_OF_RULES_ENUM; RULE_DEFS_TBL_ITER_type ri = t->crt_order()->begin(); RULE_DEFS_TBL_ITER_type rie = t->crt_order()->end(); int rule_no(0); for(;ri != rie;++ri,++Rules_enum){ rule_def* rd = *ri; ++rule_no; rd->enum_id(Rules_enum); rd->rule_no(rule_no); } @*3 Driver of |process_rules_phrase|. @+= bool process_rules_phrase@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_rules& Phrase_to_parse@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { @; using namespace NS_yacco2_terminals; using namespace yacco2; using namespace NS_rules_phrase; TOKEN_GAGGLE op1; yacco2::INT start_pos = Calling_parser.current_token_pos__; token_container_type* ip1 = Calling_parser.token_supplier(); TOKEN_GAGGLE*er1= (TOKEN_GAGGLE*)Calling_parser.error_queue(); Crules_phrase rule_ph; Parser p1(rule_ph,ip1,&op1,start_pos,er1); p1.parse(); if(er1->empty() == YES){ *Cont_pos = p1.current_token_pos__; *Cont_tok = p1.current_token(); TOKEN_GAGGLE_ITER i = op1.begin(); CAbs_lr1_sym* sym = *i; @=T_rules_phrase* t = (T_rules_phrase*)sym@>; Phrase_to_parse.rules_phrase(t); if(O2_RULES_PHASE != 0){ CAbs_lr1_sym* sym = new Err_already_processed_rule_phase; sym->set_rc(Phrase_to_parse); p1.add_token_to_error_queue(*sym); goto error_exit; } O2_RULES_PHASE= t; AST* gt = t->phrase_tree(); BUILD_GRAMMAR_TREE(*gt); @; return Success; } error_exit: lrclog << "error in rules-phrase" << std::endl; return Failure;// error placed in |error_queue| by called thread } @** External routines. @*2 Yacco2 Parse command line: |YACCO2_PARSE_CMD_LINE|.\fbreak The parameters have been extracted from the program run environment and placed into Yacco2's holding file which is hardwired by |Yacco2_holding_file| definition. Now it's time to parse them for kosherness. Constraints:\fbreak \ptindent{ip1-4: switches used to compile grammar} \ptindent{ip8: To compile grammar name extracted from parameters} \ptindent{ip9: Error token container for generated error token} Errors:\fbreak \ptindent{1) bad filename} \ptindent{2) parameters errors} @+= extern void YACCO2_PARSE_CMD_LINE@/ (yacco2::CHAR& T_sw,yacco2::CHAR& ERR_sw@/ ,yacco2::CHAR& PRT_sw@/ ,std::string& Grammar_to_compile@/ ,yacco2::TOKEN_GAGGLE& Error_queue)@/ { using namespace NS_yacco2_err_symbols; using namespace yacco2; tok_can Cmd1_tokens(Yacco2_holding_file); if(Cmd1_tokens.file_ok() == NO){ yacco2::Delete_tokens(Cmd1_tokens.container()); CAbs_lr1_sym* sym = new Err_bad_filename(Yacco2_holding_file); sym->set_external_file_id(1); sym->set_line_no(1); sym->set_pos_in_line(1); Error_queue.push_back(*sym); return; } using namespace NS_o2_lcl_opts; TOKEN_GAGGLE lcl_options_tokens; Co2_lcl_opts opts_fsm; Parser options(opts_fsm,&Cmd1_tokens,&lcl_options_tokens,0,&Error_queue,0,0); options.parse(); yacco2::Delete_tokens(Cmd1_tokens.container()); if(Error_queue.empty() != YES) return; T_sw = opts_fsm.t_sw_;ERR_sw = opts_fsm.err_sw_; PRT_sw = opts_fsm.prt_sw_; Grammar_to_compile += opts_fsm.file_to_compile_; } @*2 |BUILD_GRAMMAR_TREE| while parsing grammar.\fbreak Builds a single level tree having the following forests: all the parse phrases: for example |FSM_PHASE| etc. With the introduction of |cweb| comments, a |cweb| holding queue of comments is kept so that the comments can placed into the following phrase. Why? For example, a series of |cweb| comments could act as an introduction before the ``fsm'' phrase is detected by the lexical grammar |pass3| by its ``fsm'' keyword. So what do u do with the orphaned |cweb| comments recognized by |pass3| before the ``fsm'' phrase parse? Well call |BUILD_GRAMMAR_TREE| and discriminate what is passed to it. If it's a |cweb| comment put it into a holding list. When |BUILD_GRAMMAR_TREE| is called by a phrase process, only then graft it into the passed phrase terminal. The only exception to this is |cweb| comments after the end of the grammar's phrases have been parsed. This comment is then placed as the last node in the grammar tree. @+= extern void BUILD_GRAMMAR_TREE(yacco2::AST& Item)@/ { using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; using namespace yacco2; CAbs_lr1_sym* sym = AST::content(Item); static AST* cur_node(0); static AST* holding_kcweb(0); static AST* end_holding_kcweb(0); static bool rules_phrase_seen(false); if(sym->enumerated_id__ == T_Enum::T_T_cweb_marker_){ if(rules_phrase_seen == true){ AST::join_sts(*cur_node,Item); cur_node = &Item; return; } if(holding_kcweb == 0){ holding_kcweb = &Item; }else{ if(end_holding_kcweb == 0){ end_holding_kcweb = &Item; AST::join_sts(*holding_kcweb,*end_holding_kcweb); }else{ AST::join_sts(*end_holding_kcweb,Item); end_holding_kcweb = &Item; } } return; } if(cur_node == 0) { CAbs_lr1_sym* gp = new T_grammar_phrase(); gp->set_line_no_and_pos_in_line(*sym); gp->set_external_file_id(sym->tok_co_ords__.external_file_id__); GRAMMAR_TREE = new AST(*gp); AST::crt_tree_of_1son(*GRAMMAR_TREE,Item); cur_node = &Item; }else{ AST::join_sts(*cur_node,Item); cur_node = &Item; } if(sym->enumerated_id__ == T_Enum::T_T_rules_phrase_){ rules_phrase_seen = true; } @; } @ Handle the |cweb| comments that prefix phrases.\fbreak |cweb| comments are chained forests where the first comment will get placed into the calling phrase. i use the grammar to elegantly handle the the various phrase rather than casing them. @= if(holding_kcweb == 0) return; tok_can_ast_functor just_walk_functr; ast_prefix_1forest element_walk(Item,&just_walk_functr); tok_can phrase_can(element_walk); using namespace NS_cweb_put_k_into_ph; Ccweb_put_k_into_ph cweb_k_fsm; cweb_k_fsm.initialize(holding_kcweb,&rules_phrase_seen); Parser cweb_k(cweb_k_fsm,&phrase_can,0); cweb_k.parse(); holding_kcweb = 0; end_holding_kcweb = 0; @*2 |LOAD_YACCO2_KEYWORDS_INTO_STBL|.\fbreak Basic housekeeping. Originally a grammar recognized keywords by being in competition with the Identifier thread. Keyword thread only ran if its first set matched the starting character making up an identifier and keyword. Now it's blended into Identifier using the symbol table lookup that returns not only the identifier terminal but all other keyword entries put into the symbol table. For now, only the keywords are cloned off as unique entities whilst all other entries are passed back from their symbol table with its source co-ordinates being overriden. Why cloning of keywords? Cuz of their containment properties --- e.g., syntax code directives. Why the kludge in |load_kw_into_tbl|? The problem is a keyword like ``fsm'' cannot be represented as a terminal with the same literal name ``fsm''. So i prefixed the terminal's literal name as ``$\#$fsm'' allowing the T alphabet parse to go thru. Without this change, the symbol table would return the terminal's literal name as the keyword ``fsm'' which is a parse error. The symbol table contains the real literal name whilst the terminal has its symbolic name sake. To condense the loading of the table code, i use the terminal's literal name and guard against its symbolic name. @+= void load_kw_into_tbl(yacco2::CAbs_lr1_sym* Kw){ using namespace yacco2_stbl; T_sym_tbl_report_card report_card; const char* kwkey = Kw->id__; if(*kwkey == '#')++kwkey;// kludge: bypass 1st char eg "$\#$fsm" kw_in_stbl* kw = new kw_in_stbl(Kw); add_sym_to_stbl(report_card,*kwkey,*kw,table_entry::defed,table_entry::keyword); kw->stbl_idx(report_card.pos_); } extern void LOAD_YACCO2_KEYWORDS_INTO_STBL()@/ { using namespace yacco2_stbl; load_kw_into_tbl(new T_raw_characters); load_kw_into_tbl(new T_lr1_constant_symbols); load_kw_into_tbl(new T_error_symbols); load_kw_into_tbl(new T_eocode); load_kw_into_tbl(new T_AD); load_kw_into_tbl(new T_AB); load_kw_into_tbl(new T_parallel_la_boundary); load_kw_into_tbl(new T_arbitrator_code); load_kw_into_tbl(new T_parallel_parser); load_kw_into_tbl(new T_parallel_thread_function); load_kw_into_tbl(new T_parallel_control_monitor); load_kw_into_tbl(new T_fsm); load_kw_into_tbl(new T_fsm_id); load_kw_into_tbl(new T_fsm_filename); load_kw_into_tbl(new T_fsm_namespace); load_kw_into_tbl(new T_fsm_class); load_kw_into_tbl(new T_fsm_version); load_kw_into_tbl(new T_fsm_date); load_kw_into_tbl(new T_fsm_debug); load_kw_into_tbl(new T_fsm_comments); load_kw_into_tbl(new T_terminals); load_kw_into_tbl(new T_enumeration); load_kw_into_tbl(new T_file_name); load_kw_into_tbl(new T_name_space); load_kw_into_tbl(new T_sym_class); load_kw_into_tbl(new T_rules); load_kw_into_tbl(new T_lhs); load_kw_into_tbl(new T_user_declaration); load_kw_into_tbl(new T_user_prefix_declaration); load_kw_into_tbl(new T_user_suffix_declaration); load_kw_into_tbl(new T_constructor); load_kw_into_tbl(new T_destructor); load_kw_into_tbl(new T_op); load_kw_into_tbl(new T_failed); load_kw_into_tbl(new T_user_implementation); load_kw_into_tbl(new T_user_imp_tbl); load_kw_into_tbl(new T_user_imp_sym); load_kw_into_tbl(new T_constant_defs); load_kw_into_tbl(new T_terminals_refs); load_kw_into_tbl(new T_terminals_sufx); load_kw_into_tbl(new T_lrk_sufx); load_kw_into_tbl(new T_NULL); } @*2 Beauty and the tree: |PRINT_RULES_TREE_STRUCTURE|.\fbreak Print out the tree's contents. It is a function that works with \Yacco2's |prt_ast_functor| and a tree walker like |ast_prefix|. See \O2 documentation for an example of use and \Yacco2 documentation on functors and tree traversals --- good stuff. @+= extern void PRINT_RULES_TREE_STRUCTURE(AST* Node)@/ { using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; if(Node == 0) return; CAbs_lr1_sym* sym = AST::content(*Node); if(sym == 0) { yacco2::lrclog << " SYMBOL IN AST CONTENT NULL" << " Node*: " << Node << endl; return; } yacco2::lrclog << sym->id__ << ' '; switch (sym->enumerated_id__){ case T_Enum::T_refered_T_:{@/ @=refered_T* sym1 = (refered_T*)sym;@>@/ T_in_stbl* T = sym1->t_in_stbl(); yacco2::lrclog << T->t_def()->t_name()->c_str(); break; } case T_Enum::T_refered_rule_:{@/ @=refered_rule* sym1 = (refered_rule*)sym;@>@/ rule_in_stbl* R = sym1->Rule_in_stbl(); yacco2::lrclog << R->r_def()->rule_name()->c_str(); break; } case T_Enum::T_T_identifier_:{@/ @=T_identifier* sym1 = (T_identifier*)sym;@>@/ yacco2::lrclog << sym1->identifier()->c_str(); break; } case T_Enum::T_rule_def_:{@/ @=rule_def* sym1 = (rule_def*)sym;@>@/ yacco2::lrclog << sym1->rule_name()->c_str(); yacco2::lrclog << " epsilon: "; if(sym1->epsilon() == true){ yacco2::lrclog << " Y " ; }else{ yacco2::lrclog << " N " ; } break; } case T_Enum::T_T_terminal_def_:{@/ @=T_terminal_def* sym1 = (T_terminal_def*)sym;@>@/ yacco2::lrclog << sym1->t_name()->c_str(); break; } case T_Enum::T_T_subrule_def_:{@/ @=T_subrule_def* sym1 = (T_subrule_def*)sym;@>@/ yacco2::lrclog << " epsilon: "; if(sym1->epsilon() == true){ yacco2::lrclog << " Y " ; }else{ yacco2::lrclog << " N " ; } break; } case T_Enum::T_T_eosubrule_:{@/ break; } case T_Enum::T_T_NULL_:{@/ break; } case T_Enum::T_T_2colon_:{@/ break; } default:{ yacco2::lrclog << sym->id__; } } yacco2::lrclog << " file# " << sym->tok_co_ords__.external_file_id__ << ':' << sym->tok_co_ords__.rc_pos__ << ':'; yacco2::lrclog << " line# " << sym->tok_co_ords__.line_no__ << ':' << sym->tok_co_ords__.pos_in_line__ << ':'; yacco2::lrclog << " sym*: " << sym << ' '; yacco2::lrclog << endl; } @*2 |WRT_CWEB_MARKER|.\fbreak Just write out to a cweave file the cweb comments. @+= extern void WRT_CWEB_MARKER(std::ofstream* Wfile,yacco2::AST* Cweb_marker){ using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; using namespace yacco2; INT_SET_type filter; filter.insert(T_Enum::T_T_cweb_comment_); tok_can_ast_functor just_walk_functr; ast_prefix_1forest rule_walk(*Cweb_marker,&just_walk_functr,&filter,ACCEPT_FILTER); tok_can comments_can(rule_walk); for(int x(0);comments_can[x] != yacco2::PTR_LR1_eog__;++x){ T_cweb_comment* k = (T_cweb_comment*)comments_can[x]; (*Wfile) << k->comment_data()->c_str() << endl; } } @*2 ?Beauty and the tree: |PRINT_GRAMMAR_TREE|.\fbreak Print out the grammar's contents. This includes the |cweb| contents. It is a function that works with \Yacco2's |prt_ast_functor| and a tree walker like |ast_prefix|. See \O2 documentation for an example of use and \Yacco2 documentation on functors and tree traversals --- good stuff. @+= extern void PRINT_GRAMMAR_TREE(AST* Node)@/ { using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; if(Node == 0) return; CAbs_lr1_sym* sym = AST::content(*Node); if(sym == 0) { yacco2::lrclog << " SYMBOL IN AST CONTENT NULL" << " Node*: " << Node << " lt*: " << Node->lt_ << " rt*: " << Node->rt_ << std::endl; return; } yacco2::lrclog << sym->id__ << ' '; switch (sym->enumerated_id__){ case T_Enum::T_T_cweb_comment_:{@/ @=T_cweb_comment* sym1 = (T_cweb_comment*)sym;@>@/ yacco2::lrclog << sym1->comment_data()->c_str(); break; } } yacco2::lrclog << " file# " << sym->tok_co_ords__.external_file_id__ << ':' << sym->tok_co_ords__.rc_pos__ << ':'; yacco2::lrclog << " line# " << sym->tok_co_ords__.line_no__ << ':' << sym->tok_co_ords__.pos_in_line__ << ':'; yacco2::lrclog << " sym*: " << sym << ' '; yacco2::lrclog << " Node*: " << Node << " lt*: " << Node->lt_ << " rt*: " << Node->rt_ << std::endl; } @*2 Process Nested include files: |PROCESS_INCLUDE_FILE|.\fbreak This routine gets called from the |pass3| grammar when Yacco2's include file expression has been parsed. It uses the hardwired definition |Nested_file_cnt_limit|. The one important point is how it appends the newly processed include tokens to the calling parser's producer container. |pass3| has a global variable to ensure that only 2 |PTR_LR1_eog__| tokens are emitted to the producer container from the first |pass3| invocation. All other recursively invoked |pass3| guard against these extra end-of-grammar tokens when they finish parsing. The global |yacco2::FILE_CNT__| is incremented within the |tok_can| container. This global is just a file counter of files opened by the template container. To cope with recursion, there is the global |yacco2::STK_FILE_NOS__|. This ``call stack'' global maintains the currently opened files so that tokens generated are associated with the top file. Error reporting then GPSs to the source line and character position within this tagged file when an error token is created using this source file coordinates taken from the faulty T token. It's depth is tested for file recursion overrun. Constraints:\fbreak \ptindent{ip1: Calling parser environment} \ptindent{ip2: include token to process} \ptindent{ip3: Token container to append to} Note: The filename to process has already had its existence check done by the |T_file_inclusion| token. Why the passing of the |Calling_parser|? There are 2 reasons all related to error processing. Firstly, the error queue is needed and secondly to abort the |Calling_parser|. Though the returned result also indicates success or failure, I thought that it was better to indicate by action rather than intent what should be done. This is a little draconian but... Error processing:\fbreak \ptindent{op1: nested file limit is exceeded} \ptindent{op2: bad file name} Note: if an error occurs, the calling parser is aborted. No error correction is done. It stops the processing immediately so that the error can be reported back to the grammar writer. @+= extern bool PROCESS_INCLUDE_FILE@/ (yacco2::Parser& Calling_parser@/ ,NS_yacco2_terminals::T_file_inclusion& File_include@/ ,yacco2::token_container_type& T2)@/ { using namespace NS_yacco2_terminals; using namespace NS_yacco2_err_symbols; using namespace yacco2; std::string* ps_fn = File_include.file_name()->c_string(); lrclog << "Pre-process file: " << ps_fn->c_str() << std::endl; if(yacco2::STK_FILE_NOS__.size() >= Nested_file_cnt_limit){ CAbs_lr1_sym* s = new Err_nested_files_exceeded(Nested_file_cnt_limit,*ps_fn); s->set_line_no_and_pos_in_line(File_include); s->set_external_file_id(File_include.tok_co_ords__.external_file_id__); Calling_parser.add_token_to_error_queue(*s); Calling_parser.abort_parse__; return Failure; } tok_can p1_tokens(ps_fn->c_str()); if(p1_tokens.file_ok() == NO){ yacco2::Delete_tokens(p1_tokens.container()); CAbs_lr1_sym* sym = new NS_yacco2_err_symbols::Err_bad_filename(*ps_fn); sym->set_external_file_id(File_include.tok_co_ords__.external_file_id__); sym->set_line_no_and_pos_in_line(File_include); Calling_parser.add_token_to_error_queue(*sym); Calling_parser.abort_parse__; return Failure; } using namespace NS_pass3; Cpass3 p3_fsm; Parser pass3(p3_fsm,&p1_tokens,&T2,0,Calling_parser.error_queue(),Calling_parser.recycle_bin__); pass3.parse(); yacco2::Delete_tokens(p1_tokens.container()); if(pass3.error_queue()->empty() != YES){ Calling_parser.abort_parse__; return Failure; } yacco2::STK_FILE_NOS__.pop_back(); return Success; } @*2 Process syntax code: |PROCESS_KEYWORD_FOR_SYNTAX_CODE|.\fbreak This is the dispatch routine called from the |pass3| grammar per phase to parse. What you will see within the |pass3.lex| grammar is a subrule with the appropriate keyword. Reality is the grammar thread |identifier| returns a carrier terminal called |keyword|. Inside it is the individual keyword recognized that must be extracted by the syntax directed code. This was done to lower the amount of subrules within |pass3|. This is the partial truth; evolution in the wild card facility also lowers the explicit programming of subrules. The extracted keyword is passed to this routine for processing. If a success is returned, the extracted keyword is put into the output container while an error stops the parse with the error placed into the `error queue'. The carrier terminal |keyword| has the `AD' auto delete attribute defined so |keyword| is deleted when it is popped from the parse stack. This is a simple way to deal with individual components. Within each of the to-be-parsed phases, their syntax is gramatically verified. As it comes out of a lexical grammar, down the road, the syntactic part must check for the proper sequencing of these phases. For example, the production component cannot come before the LRk phase etc. Due to a fixed ordering of T vocabulary components enumeration, T's components must be in lrk,rc,user, and error terminals parse order as i add their forests to the grammar tree while they are being build! Out of order will violate the T vocabulary's enumeration as i walk the tree as created when enumerating. @+= extern bool PROCESS_KEYWORD_FOR_SYNTAX_CODE@/ (yacco2::Parser& Parser@/ ,yacco2::CAbs_lr1_sym* Keyword@/ ,yacco2::CAbs_lr1_sym** Cont_tok@/ ,yacco2::INT* Cont_pos)@/ { using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; using namespace yacco2; switch (Keyword->enumerated_id__){ case T_Enum::T_T_fsm_ :{ @=T_fsm* fsm = (T_fsm*)Keyword@>; return process_fsm_phrase(Parser,*fsm,Cont_tok,Cont_pos); } case T_Enum::T_T_parallel_parser_ :{ @=T_parallel_parser* pparser = (T_parallel_parser*)Keyword@>; return process_parallel_parser_phrase(Parser,*pparser,Cont_tok,Cont_pos); } case T_Enum::T_T_enumeration_ :{ @=T_enumeration* enumer = (T_enumeration*)Keyword@>; return process_T_enum_phrase(Parser,*enumer,Cont_tok,Cont_pos); } case T_Enum::T_T_error_symbols_ :{ @=T_error_symbols* err = (T_error_symbols*)Keyword@>; return process_error_symbols_phrase(Parser,*err,Cont_tok,Cont_pos); } case T_Enum::T_T_raw_characters_ :{ @=T_raw_characters* rc = (T_raw_characters*)Keyword@>; return process_rc_phrase(Parser,*rc,Cont_tok,Cont_pos); } case T_Enum::T_T_lr1_constant_symbols_ :{ @=T_lr1_constant_symbols* lr = (T_lr1_constant_symbols*)Keyword@>; return process_lr1_k_phrase(Parser,*lr,Cont_tok,Cont_pos); } case T_Enum::T_T_terminals_ :{ @=T_terminals* term = (T_terminals*)Keyword@>; return process_terminals_phrase(Parser,*term,Cont_tok,Cont_pos); } case T_Enum::T_T_rules_ :{ @=T_rules* rule = (T_rules*)Keyword@>; return process_rules_phrase(Parser,*rule,Cont_tok,Cont_pos); } default: { CAbs_lr1_sym* sym = new Err_not_kw_defining_grammar_construct; sym->set_rc(*Keyword); Parser.add_token_to_error_queue(*sym); return Failure; } } return Success; } @** Mpost and Cweb Routines.\fbreak These external routines simplify the |cweave| code generated for the |mpost_output| grammar. As i do not parse the syntax code into appropriate structures but build up strings of characters, at least i can beautify the emitted |cweave| grammar code by use of external rountines that are under |cweb|'s generation. @*2 |MPOST_CWEB_LOAD_XLATE_CHRS|.\fbreak These are the raw characters whose values are given a literal title. Why? Some raw characters will cause |cweave| to cough. So i'm attempting to avoid this clearing of the throat by character translation as I'm not wanting to parse formally the syntax code and to emit yet another properly formed language. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_LOAD_XLATE_CHRS@/ (NS_mpost_output::Cmpost_output* Fsm)@/ { Fsm->xlated_names_[T_Enum::T_raw_exclam_] = "exclamation mark"; Fsm->xlated_names_[T_Enum::T_raw_dbl_quote_] = "dbl quote"; Fsm->xlated_names_[T_Enum::T_raw_no_sign_] = "no sign"; Fsm->xlated_names_[T_Enum::T_raw_dollar_sign_] = "dollar sign"; Fsm->xlated_names_[T_Enum::T_raw_percent_] = "percent"; Fsm->xlated_names_[T_Enum::T_raw_ampersign_] = "ampersand"; Fsm->xlated_names_[T_Enum::T_raw_right_quote_] = "rt quote"; Fsm->xlated_names_[T_Enum::T_raw_open_bracket_] = "open bracket"; Fsm->xlated_names_[T_Enum::T_raw_close_bracket_] = "close bracket"; Fsm->xlated_names_[T_Enum::T_raw_asteric_] = "asterisk"; Fsm->xlated_names_[T_Enum::T_raw_plus_] = "plus"; Fsm->xlated_names_[T_Enum::T_raw_comma_] = "comma"; Fsm->xlated_names_[T_Enum::T_raw_minus_] = "minus"; Fsm->xlated_names_[T_Enum::T_raw_period_] = "period"; Fsm->xlated_names_[T_Enum::T_raw_slash_] = "slash"; Fsm->xlated_names_[T_Enum::T_raw_colon_] = "colon"; Fsm->xlated_names_[T_Enum::T_raw_semi_colon_] = "semi colon"; Fsm->xlated_names_[T_Enum::T_raw_less_than_] = "less than"; Fsm->xlated_names_[T_Enum::T_raw_eq_] = "eq"; Fsm->xlated_names_[T_Enum::T_raw_gt_than_] = "gt than"; Fsm->xlated_names_[T_Enum::T_raw_question_mark_] = "question mark"; Fsm->xlated_names_[T_Enum::T_raw_at_sign_] = "at sign"; Fsm->xlated_names_[T_Enum::T_raw_open_sq_bracket_] = "open sq bracket"; Fsm->xlated_names_[T_Enum::T_raw_back_slash_] = "back slash"; Fsm->xlated_names_[T_Enum::T_raw_close_sq_bracket_] = "close sq bracket"; Fsm->xlated_names_[T_Enum::T_raw_up_arrow_] = "up arrow"; Fsm->xlated_names_[T_Enum::T_raw_under_score_] = "under score"; Fsm->xlated_names_[T_Enum::T_raw_left_quote_] = "left quote"; Fsm->xlated_names_[T_Enum::T_raw_open_brace_] = "open brace"; Fsm->xlated_names_[T_Enum::T_raw_vertical_line_] = "vertical line"; Fsm->xlated_names_[T_Enum::T_raw_close_brace_] = "close brace"; Fsm->xlated_names_[T_Enum::T_raw_tilde_] = "tilde"; Fsm->xlated_names_[T_Enum::T_raw_del_] = "del"; Fsm->xlated_names_[T_Enum::T_LR1_invisible_shift_operator_] = "\\invisibleshift"; Fsm->xlated_names_[T_Enum::T_LR1_all_shift_operator_] = "\\allshift"; Fsm->xlated_names_[T_Enum::T_LR1_reduce_operator_] = "\\reduceoperator"; Fsm->xlated_names_[T_Enum::T_LR1_fset_transience_operator_] = "\\transienceoperator"; Fsm->xlated_names_[T_Enum::T_LR1_questionable_shift_operator_] = "\\questionableoperator"; } @*2 |MPOST_CWEB_EMIT_PREFIX_CODE|.\fbreak Output prelude statements for |cweave| and |mpost| files. |cweave| contains basic packages and macros whilest |mpost| contains its grammar drawing macros and basic array of variables. @*3 Setup |mpost| macros and variable arrays. @= KCHARP mp_prefix_file = "% file: "; Fsm->omp_file_ << mp_prefix_file; KCHARP mp_prefix_file_value = "%s - grammar railroad diagrams for mpost program\n"; int x = sprintf(Fsm->big_buf_,mp_prefix_file_value,Fsm->mp_filename_.c_str()); Fsm->omp_file_.write(Fsm->big_buf_,x); Fsm->omp_file_ << endl; KCHARP mp_prefix_date = "% date: "; Fsm->omp_file_ << mp_prefix_date; KCHARP mp_prefix_date_value =@/ "%s\n"@/ "input \"/usr/local/yacco2/diagrams/o2diag.mp\"\n"@/ "numeric no_of_rules,Box_solid,Box_dotted,Circle_solid,Circle_dotted;\n"@/ "Box_solid:=1;Box_dotted:=2;Circle_solid:=3;Circle_dotted:=4;\n"@/ "string rule_names[].literal;\n"@/ "string rule_names[].vname;\n"@/ "numeric rule_s_no_rhs[];\n"@/ "string rhs_elems[][][].literal;\n"@/ "string rhs_elems[][][].vname;\n"@/ "numeric rhs_elems[][][].Drw_how;\n"@/ "numeric rule_s_subrule_no_elems[][];\n"; x = sprintf(Fsm->big_buf_,mp_prefix_date_value,Fsm->gened_date_time_.c_str()); Fsm->omp_file_.write(Fsm->big_buf_,x); @*3 Setup |cweave| macros and odds and ends. @= KCHARP w_prefix_file = "% file: "; Fsm->ow_file_ << w_prefix_file; KCHARP w_prefix_filename_value = "%s - cweb grammar\n"; x = sprintf(Fsm->big_buf_,w_prefix_filename_value,Fsm->w_filename_.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); KCHARP w_prefix_date = "%Date: "; Fsm->ow_file_ << w_prefix_date; KCHARP w_prefix_date_value = "%s\n"; x = sprintf(Fsm->big_buf_,w_prefix_date_value,Fsm->gened_date_time_.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); KCHARP macros = "\\input%s \"supp-pdf\"\n"@/ "\\input \"/usr/local/yacco2/diagrams/o2mac.tex\"\n"; x = sprintf(Fsm->big_buf_,macros," "); Fsm->ow_file_.write(Fsm->big_buf_,x); @*2 Driver of |MPOST_CWEB_EMIT_PREFIX_CODE|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_EMIT_PREFIX_CODE@/ (NS_mpost_output::Cmpost_output* Fsm)@/ { @; @; } @*2 |MPOST_CWEB_gen_dimension_name|.\fbreak Generates the string literal for a dimension --- rule no, subrule no, or element no. It's out string is used to build up the |mpost|'s variable object literal name. The base number is 27 but as the elements are 1 and greater the outputed string is ${26^2}$ of 2 digits. So rule 27, subrule 3, element 5 produces 3 strings of ``ba'', ``ac'', and ``ae''. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_gen_dimension_name@/ (Cmpost_output* Fsm,std::string& Mp_obj_name,int Dimension){@/ int R,Q; R = Dimension%27; Q = Dimension/27; ++Q; Mp_obj_name += Fsm->mp_dimension_[Q]; Mp_obj_name += Fsm->mp_dimension_[R]; } @*2 |MPOST_CWEB_calc_mp_obj_name|.\fbreak Build up the 3 dimension name for |mpost|'s object name. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_calc_mp_obj_name@/ (Cmpost_output* Fsm,std::string& Mp_obj_name,int Elem_no){@/ Fsm->MPOST_CWEB_gen_dimension_name(Fsm,Mp_obj_name,Fsm->rule_no_); Fsm->MPOST_CWEB_gen_dimension_name(Fsm,Mp_obj_name,Fsm->subrule_no_); Fsm->MPOST_CWEB_gen_dimension_name(Fsm,Mp_obj_name,Elem_no); } @*2 |MPOST_CWEB_wrt_mp_rhs_elem|.\fbreak Output |mpost|'s statements for a subrule's element. Due to the |convertMPtoPDF| marco having problems with embedded space within literal names, i do a substitution on spaces for ``.'' to keep the \TeX flowing. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_mp_rhs_elem@/ (Cmpost_output* Fsm,std::string& Elem_name,std::string& Drw_how){@/ std::string mp_xlate_name; prescan_mpname_for_cweb(Elem_name,mp_xlate_name); std::string mp_obj_name; KCHARP mp_subrule_elems_literal = "rhs_elems[%i][%i][%i].literal := \"%s\";"; int x = sprintf(Fsm->big_buf_,mp_subrule_elems_literal ,Fsm->rule_no_,Fsm->subrule_no_,Fsm->elem_no_ ,mp_xlate_name.c_str()); Fsm->omp_file_.write(Fsm->big_buf_,x); Fsm->omp_file_<< endl; KCHARP mp_subrule_elems_RorT = "rhs_elems[%i][%i][%i].Drw_how := %s;"; x = sprintf(Fsm->big_buf_,mp_subrule_elems_RorT,Fsm->rule_no_,Fsm->subrule_no_,Fsm->elem_no_ ,Drw_how.c_str()); Fsm->omp_file_.write(Fsm->big_buf_,x); Fsm->omp_file_<< endl; KCHARP mp_subrule_elems_vname = "rhs_elems[%i][%i][%i].vname := \"%s\";"; Fsm->MPOST_CWEB_calc_mp_obj_name(Fsm,mp_obj_name,Fsm->elem_no_); x = sprintf(Fsm->big_buf_,mp_subrule_elems_vname,Fsm->rule_no_,Fsm->subrule_no_,Fsm->elem_no_ ,mp_obj_name.c_str()); Fsm->omp_file_.write(Fsm->big_buf_,x); Fsm->omp_file_<< endl; } @*2 |MPOST_CWEB_gen_sr_elem_xrefs|.\fbreak To aid the grammar writer, all vocubulary symbols are cross referencesd using |cweave|'s directive. Originally each subrule was redrawn having the cross reference specific to the subrule. As there was too much spam being generated for aireous subrules with no syntax directed code, i decided to redraw subrules only having code. Consequently the cross references go against the rule definition rather than its specific subrules.\fbreak \fbreak |cweave| doesn't xref the 1 characters so i bypass them as i do not want to change the characteristics of it... for now ... @*4 Establish tree container with appropriate element filters. @= using namespace yacco2; using namespace NS_yacco2_T_enum; using namespace NS_yacco2_terminals; KCHARP xref = "@@.%s@@>\n"; INT_SET_type filter; filter.insert(T_Enum::T_refered_T_); filter.insert(T_Enum::T_refered_rule_); filter.insert(T_Enum::T_T_eosubrule_); filter.insert(T_Enum::T_T_called_thread_eosubrule_); filter.insert(T_Enum::T_T_null_call_thread_eosubrule_); filter.insert(T_Enum::T_LR1_parallel_operator_); filter.insert(T_Enum::T_LR1_fset_transience_operator_); tok_can_ast_functor just_walk_functr; ast_prefix_1forest element_walk(*Subrule_tree,&just_walk_functr,&filter,ACCEPT_FILTER); tok_can elements_can(element_walk); @*4 Walk the container and produce the elements' cross references. @= for(int x(0);elements_can[x] != yacco2::PTR_LR1_eog__;++x){ ++elem_cnt; CAbs_lr1_sym* sym = elements_can[x]; switch (sym->enumerated_id__){ case T_Enum::T_LR1_parallel_operator_: { int x = sprintf(Fsm->big_buf_,xref,"\\paralleloperator"); Fsm->ow_file_.write(Fsm->big_buf_,x); break; } case T_Enum::T_LR1_fset_transience_operator_: { int x = sprintf(Fsm->big_buf_,xref,"\\transienceoperator"); Fsm->ow_file_.write(Fsm->big_buf_,x); break; } case T_Enum::T_refered_T_: { @=refered_T* rt = (refered_T*)sym@>; Fsm->MPOST_CWEB_xref_refered_T(Fsm,rt); break; } case T_Enum::T_refered_rule_: { @=refered_rule* rr = (refered_rule*)sym@>; Fsm->MPOST_CWEB_xref_refered_rule(Fsm,rr); break; } case T_Enum::T_T_eosubrule_: { if(elem_cnt == 1){ int x = sprintf(Fsm->big_buf_,xref,"\\emptyrule"); Fsm->ow_file_.write(Fsm->big_buf_,x); } break; } case T_Enum::T_T_called_thread_eosubrule_: { @=T_called_thread_eosubrule* id = (T_called_thread_eosubrule*)sym@>; if(id->ns() !=0){// \\TRAshift call has no namespace :: qualifiers th_name += id->ns()->identifier()->c_str(); th_name+= "::"; } th_name += id->called_thread_name()->identifier()->c_str(); string name_with_bkslash; int len = th_name.length(); for(int x=0;xbig_buf_,xref,name_with_bkslash.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); break; } case T_Enum::T_T_null_call_thread_eosubrule_: { int x = sprintf(Fsm->big_buf_,xref,"NULL"); Fsm->ow_file_.write(Fsm->big_buf_,x); break; } } } @*3 Driver of |MPOST_CWEB_gen_sr_elem_xrefs|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_gen_sr_elem_xrefs@/ (Cmpost_output* Fsm,AST* Subrule_tree){@/ @; int elem_cnt(0); string th_name; @; } @*2 |MPOST_CWEB_xlated_symbol|.\fbreak @+= extern int MPOST_CWEB_xlated_symbol(AST* Sym_t,char* Xlated_sym){@/ CAbs_lr1_sym* Sym = AST::content(*Sym_t); int Enum = Sym->enumerated_id(); Xlated_sym[0] = (char)0; const char* xsym = "%s"; switch (Enum){ case T_Enum::T_LR1_parallel_operator_: { sprintf(Xlated_sym,xsym,"\\paralleloperator"); return 1; } case T_Enum::T_LR1_fset_transience_operator_: { sprintf(Xlated_sym,xsym,"\\transienceoperator"); return 1; } case T_Enum::T_refered_T_: { @=refered_T* rt = (refered_T*)Sym@>; XLATE_SYMBOLS_FOR_cweave(rt->its_t_def()->t_name()->c_str(),Xlated_sym); return rt->element_pos(); } case T_Enum::T_refered_rule_: { @=refered_rule* rr = (refered_rule*)Sym@>; XLATE_SYMBOLS_FOR_cweave(rr->its_rule_def()->rule_name()->c_str(),Xlated_sym); return rr->element_pos(); } case T_Enum::T_T_eosubrule_: {// if parallel expr then use eosurule of thread @=T_eosubrule* eos = (T_eosubrule*)Sym@>; AST* prev_t = Sym_t->pr_; if(eos->element_pos() == 1){ sprintf(Xlated_sym,xsym,"\\emptyrule"); return eos->element_pos(); }else{ sprintf(Xlated_sym,xsym,""); } CAbs_lr1_sym* sym = AST::content(*prev_t); switch (sym->enumerated_id()){ case T_Enum::T_T_called_thread_eosubrule_:{ return eos->element_pos() -1; } case T_Enum::T_T_null_call_thread_eosubrule_:{ return eos->element_pos() -1; } default:{ return eos->element_pos(); } } } case T_Enum::T_T_called_thread_eosubrule_: { @=T_called_thread_eosubrule* id = (T_called_thread_eosubrule*)Sym@>; string th_name; if(id->ns() !=0){// \\TRAshift call has no namespace :: qualifiers th_name += id->ns()->identifier()->c_str(); th_name+= "::"; } th_name += id->called_thread_name()->identifier()->c_str(); XLATE_SYMBOLS_FOR_cweave(th_name.c_str(),Xlated_sym); return id->element_pos(); } case T_Enum::T_T_null_call_thread_eosubrule_: { @=T_null_call_thread_eosubrule* id = (T_null_call_thread_eosubrule*)Sym@>; sprintf(Xlated_sym,xsym,"NULL"); return id->element_pos(); } } return 0;// fake it: cuz apple's latest compiler: symantic error-never reached! } @*2 |MPOST_CWEB_crt_rhs_sym_str| - follow set contributing symbols.\fbreak Generate the state's subrule symbols in for cweave to digest. If the first symbol in the string is a rule, this rule's follow set string gets highlighted by underlining the symbols that contribute to it. It will transient walk though these follow symbols if they are epsilonabe rules. This underline string of symbols will use the ``...''' symbol to indicate more symbols contribute but due to the report's space constrains are not reported. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_crt_rhs_sym_str@/ (state_element* se,std::string* Xlated_str){ char a[BUFFER_SIZE]; char cur_sym[Max_cweb_item_size]; char nxtsym_1[Max_cweb_item_size]; char nxtsym_2[Max_cweb_item_size]; KCHARP thread_expr_string_template = "{%s} {%s} {%s}";// 3 symbols: current and 2 la KCHARP underline_symbol = "{$\\underline{%s}$} \n"; KCHARP underline_symbol_wepsilon = "{$\\underline{%s^{\\emptyrule}}$} \n"; KCHARP not_underline_symbol = "{%s} \n"; state_element* nxt1_se = 0; state_element* nxt2_se = 0; state_element* nxt3_se = 0; int pp = MPOST_CWEB_xlated_symbol(se->sr_element_,cur_sym); if(// chained or thread call expression (se->its_enum_id_ == T_Enum::T_LR1_parallel_operator_) || (se->its_enum_id_ == T_Enum::T_LR1_fset_transience_operator_) ){ nxt1_se = se->next_state_element_; AST* nxt2_se= se->sr_element_->rt_->rt_;// thread eos for called thd nm pp = MPOST_CWEB_xlated_symbol(nxt1_se->sr_element_,nxtsym_1); pp = MPOST_CWEB_xlated_symbol(nxt2_se,nxtsym_2); sprintf(a,thread_expr_string_template,cur_sym,nxtsym_1,nxtsym_2); (*Xlated_str) += a; return; } sprintf(a,not_underline_symbol,cur_sym); (*Xlated_str) += a; if(se->sr_def_element_->enumerated_id__ != T_Enum::T_rule_def_){ // T return; } nxt1_se = se->next_state_element_; if(nxt1_se==0) return; if(nxt1_se->its_enum_id_ == T_Enum::T_T_eosubrule_) return; pp = MPOST_CWEB_xlated_symbol(nxt1_se->sr_element_,nxtsym_1); if(nxt1_se->sr_def_element_->enumerated_id__ != T_Enum::T_rule_def_){ sprintf(a,underline_symbol,nxtsym_1); }else{ rule_def* rd = (rule_def*)nxt1_se->sr_def_element_; if(rd->epsilon() == YES){ sprintf(a,underline_symbol_wepsilon,nxtsym_1); nxt2_se = nxt1_se->next_state_element_; }else{ sprintf(a,underline_symbol,nxtsym_1); nxt2_se = 0; } } (*Xlated_str) += a; if(nxt2_se != 0){// 2nd follow set sym due to epsilon rule if(nxt2_se->its_enum_id_ == T_Enum::T_T_eosubrule_) return; pp = MPOST_CWEB_xlated_symbol(nxt2_se->sr_element_,nxtsym_2); if(nxt2_se->sr_def_element_->enumerated_id__ != T_Enum::T_rule_def_){ // T sprintf(a,underline_symbol,nxtsym_2); }else{ rule_def* rd = (rule_def*)nxt2_se->sr_def_element_; if(rd->epsilon() == YES){ sprintf(a,underline_symbol_wepsilon,nxtsym_2); nxt3_se = nxt1_se->next_state_element_; if(nxt3_se->its_enum_id_ == T_Enum::T_T_eosubrule_){ nxt3_se = 0; } }else{ sprintf(a,underline_symbol,nxtsym_2); nxt3_se = 0; } } (*Xlated_str) += a; } if(nxt3_se != 0){// ... all others due to 2nd epsilon follow symbol rule sprintf(a,not_underline_symbol,"..."); (*Xlated_str) += a; } } @*2 |MPOST_CWEB_woutput_sr_sdcode|.\fbreak |cweave|'s statements for a subrule having syntax directed code. Note: the use of a grammar to generate it. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_woutput_sr_sdcode@/ (Cmpost_output* Fsm,T_subrule_def* Subrule_def){@/ SDC_MAP_type* sr_dirs = Subrule_def->subrule_directives(); SDC_MAP_ITER_type i = sr_dirs->begin(); SDC_MAP_ITER_type ie = sr_dirs->end(); TOKEN_GAGGLE dirs_tokens; for(;i!=ie;++i){ CAbs_lr1_sym* sym = i->second; dirs_tokens.push_back(*sym); } using namespace NS_cweave_sdc; Ccweave_sdc sdc_fsm; sdc_fsm.initialize(&Fsm->ow_file_,Fsm->subrule_def_,Fsm->subrule_no_); Parser sdc_emit(sdc_fsm,&dirs_tokens,0); sdc_emit.parse(); } @*2 |MPOST_CWEB_wrt_fsm|.\fbreak |cweave|'s |fsm| part of the grammar. Not much but there's that grammar use to generate the output. @*4 Output |fsm|'s class section. @= char xa[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave (Fsm_phrase->fsm_class_phrase()->identifier()->identifier()->c_str() ,xa); KCHARP fsm_class = "@@*2 Fsm %s class.\n"; int x = sprintf(Fsm->big_buf_ ,fsm_class ,xa ); Fsm->ow_file_.write(Fsm->big_buf_,x); Fsm->ow_file_<< endl; @*4 Ready those |fsm| syntax directed code jelly beans for consumption. @= T_fsm_class_phrase* fcp = Fsm_phrase->fsm_class_phrase(); SDC_MAP_type* sr_dirs = fcp->directives_map(); SDC_MAP_ITER_type i = sr_dirs->begin(); SDC_MAP_ITER_type ie = sr_dirs->end(); TOKEN_GAGGLE dirs_tokens; for(;i!=ie;++i){ CAbs_lr1_sym* sym = i->second; dirs_tokens.push_back(*sym); } @*3 Driver of |MPOST_CWEB_wrt_fs|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_fsm@/ (Cmpost_output* Fsm,T_fsm_phrase* Fsm_phrase){@/ KCHARP banner = "\\GRAMMARtitle{%s}\n{%s}{%s}%\n" "{%s}{%s}%\n" "{%s}\n"; KCHARP banner_of_thread = "\\THREADtitle{%s}%\n" "{%s}{%s}%\n" "{%s}{%s}{%s}{%i}%\n" "{%s}\n"; char fname[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Fsm->grammar_filename_prefix_.c_str(),fname); char ns_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Fsm_phrase->namespace_id()->identifier()->c_str(),ns_name); char fsm_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Fsm_phrase->fsm_id()->c_string()->c_str(),fsm_name); char k_cweb[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(Fsm_phrase->comment()->c_string()->c_str(),k_cweb); if(O2_PP_PHASE != 0){ T_parallel_parser_phrase* la_ph = O2_PP_PHASE; T_parallel_la_boundary* la = la_ph->la_bndry(); // build srce expr char expr_prescan_cweb[Max_cweb_item_size]; int x = sprintf(Fsm->big_buf_ ,banner_of_thread ,fname ,fsm_name ,ns_name ,Fsm_phrase->version()->c_string()->c_str() ,Fsm_phrase->debug()->c_string()->c_str() ,k_cweb ,la->la_first_set()->size() //,expr\_prescan\_cweb ,la->cweb_la_srce_expr()->c_str() ); Fsm->ow_file_.write(Fsm->big_buf_,x); Fsm->ow_file_ << endl; }else{ int x = sprintf(Fsm->big_buf_ ,banner ,fname ,fsm_name ,ns_name ,Fsm_phrase->version()->c_string()->c_str() ,Fsm_phrase->debug()->c_string()->c_str() ,k_cweb ); Fsm->ow_file_.write(Fsm->big_buf_,x); Fsm->ow_file_ << endl; } if(Fsm_phrase->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_file_,Fsm_phrase->cweb_marker()); } @; @; using namespace NS_cweave_fsm_sdc; Ccweave_fsm_sdc sdc_fsm; sdc_fsm.initialize(&Fsm->ow_file_,Fsm_phrase); Parser sdc_emit(sdc_fsm,&dirs_tokens,0); sdc_emit.parse(); } @*2 |MPOST_CWEB_wrt_T|.\fbreak |cweave|'s |T| portion of grammar vocabulary. @*4 Output |T| macros and files. @= output_cweb_macros_and_banner( Fsm,Fsm->ow_t_file_,"Terminal Vocabulary",Fsm->gened_date_time_.c_str() ,T_phrase->filename_id()->identifier() ,T_phrase->namespace_id()->identifier() ,T_phrase->alphabet()->size()); @*5 hrule to end T entry.\fbreak @= KCHARP rule_it = "\\fbreak%s\n"@/ "\\hrule\n"; x = sprintf(Fsm->big_buf_,rule_it," "); Fsm->ow_t_file_.write(Fsm->big_buf_,x); @*4 Output |T|'s entry.\fbreak For now i hardwire the enumeration label here. It will be deposited in the enumeration grammar for me to fetch. @= char ab[] = "Y"; if(tdef->autoabort() == true) ab[0] = 'Y'; else ab[0] = 'N'; char ad[] = "Y"; if(tdef->autodelete() == true) ad[0]='Y'; else ad[0] = 'N'; char name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),name); char class_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->classsym()->c_str(),class_name); const char* enum_name_for_format = "T\\_%s\\_"; char a[SMALL_BUFFER_4K]; sprintf(a,enum_name_for_format,class_name); KCHARP t_entry = "@@*2 {\\bf %s}.\\fbreak\n"@/ "Enum: %s\n\\fbreak\n"@/ "\\line{Class: %s \\hfil AB: %s \\hfil AD: %s}\n"; int x = sprintf(Fsm->big_buf_ ,t_entry ,name ,a ,class_name ,ab ,ad ); Fsm->ow_t_file_.write(Fsm->big_buf_,x); Fsm->ow_t_file_<< endl; if(tdef->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_t_file_,tdef->cweb_marker()); } @; @*4 Output prefix code if present.\fbreak @= if(T_phrase->terminals_refs_code() != 0){ KCHARP tref = "@@*2 {\\bf Terminals-refs} directive.\\fbreak\n"; Fsm->ow_t_file_ << tref; T_syntax_code* stc = T_phrase->terminals_refs_code(); if(stc->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_t_file_,stc->cweb_marker()); } KCHARP tref_code =@/ "@@=\n" "%s"@/ "\n"; int x = sprintf(Fsm->big_buf_ ,tref_code ,T_phrase->terminals_refs_code()->syntax_code()->c_str() ); Fsm->ow_t_file_.write(Fsm->big_buf_,x); Fsm->ow_t_file_ << endl; } @*4 Output suffix code if present.\fbreak @= if(T_phrase->terminals_sufx_code() != 0){ KCHARP tsuf = "@@*1 {\\bf Terminals-sufx} directive.\\fbreak\n"; Fsm->ow_t_file_ << tsuf; T_syntax_code* stc = T_phrase->terminals_sufx_code(); if(stc->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_t_file_,stc->cweb_marker()); } KCHARP tsuf_code = "@@=\n"@/ "%s"@/ "\n"; int x = sprintf(Fsm->big_buf_ ,tsuf_code ,T_phrase->terminals_sufx_code()->syntax_code()->c_str() ); Fsm->ow_t_file_.write(Fsm->big_buf_,x); Fsm->ow_t_file_<< endl; } @*4 Get those |T| syntax directed code jelly beans ready for consumption. @= SDC_MAP_type* sr_dirs = tdef->directives_map(); SDC_MAP_ITER_type i = sr_dirs->begin(); SDC_MAP_ITER_type ie = sr_dirs->end(); TOKEN_GAGGLE dirs_tokens; for(;i!=ie;++i){ CAbs_lr1_sym* sym = i->second; dirs_tokens.push_back(*sym); } @*3 Driver of |MPOST_CWEB_wrt_T|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_T@/ (Cmpost_output* Fsm,T_terminals_phrase* T_phrase){@/ @; if(T_phrase->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_t_file_,T_phrase->cweb_marker()); } @; T_DEF_MAP_ITER_type a = T_phrase->alphabet()->begin(); T_DEF_MAP_ITER_type ae = T_phrase->alphabet()->end(); char term_name[Max_cweb_item_size]; for(;a!=ae;++a){ T_terminal_def* tdef = a->second; @; @; using namespace NS_cweave_T_sdc; Ccweave_T_sdc sdc_fsm; XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),term_name); sdc_fsm.initialize(&Fsm->ow_t_file_,term_name); Parser sdc_emit(sdc_fsm,&dirs_tokens,0); sdc_emit.parse(); } @; Fsm->ow_t_file_ << "@@** Index.\n"; } @*2 |MPOST_CWEB_wrt_Err|.\fbreak |cweave|'s |Err| portion of grammar vocabulary. @*4 Output |err| macros and files. @= output_cweb_macros_and_banner( Fsm,Fsm->ow_err_file_,"Error Vocabulary",Fsm->gened_date_time_.c_str() ,Err_phrase->filename_id()->identifier() ,Err_phrase->namespace_id()->identifier() ,Err_phrase->alphabet()->size()); @*5 Err hrule to end T entry.\fbreak @= KCHARP rule_it = "\\fbreak%s\n"@/ "\\hrule\n"; x = sprintf(Fsm->big_buf_,rule_it," "); Fsm->ow_err_file_.write(Fsm->big_buf_,x); @*4 Output |err|'s entry.\fbreak Due to my verbosity and the black slug outputted from |cweave| / \TeX, i removed the emitting of the enumeration symbol as part of the index. For now i have not tested against the enumeration symbol. i explicitly reference it by its key from a returned thread call or i use the \ALLshift to catch it. Time may squelch my decision. @= char ab[] = "Y"; if(tdef->autoabort() == true) ab[0] = 'Y'; else ab[0] = 'N'; char ad[] = "Y"; if(tdef->autodelete() == true) ad[0] = 'Y'; else ad[0] = 'N'; char name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),name); char class_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->classsym()->c_str(),class_name); const char* enum_name_for_format = "T\\_%s\\_"; char enum_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->classsym()->c_str(),enum_name); char a[SMALL_BUFFER_4K]; sprintf(a,enum_name_for_format,enum_name); KCHARP t_entry = "@@*2 {\\bf %s}.\\fbreak\n"@/ "Enum: %s\n\\fbreak\n"@/ "\\line{Class: %s \\hfil AB: %s \\hfil AD: %s}\n"; int x = sprintf(Fsm->big_buf_ ,t_entry ,name ,a ,class_name ,ab ,ad ); Fsm->ow_err_file_.write(Fsm->big_buf_,x); Fsm->ow_err_file_ << endl; if(tdef->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_err_file_,tdef->cweb_marker()); } @; @*3 Driver of |MPOST_CWEB_wrt_Err|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_Err@/ (Cmpost_output* Fsm,T_error_symbols_phrase* Err_phrase){@/ @; if(Err_phrase->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_err_file_,Err_phrase->cweb_marker()); } T_DEF_MAP_ITER_type a = Err_phrase->alphabet()->begin(); T_DEF_MAP_ITER_type ae = Err_phrase->alphabet()->end(); char term_name[Max_cweb_item_size]; for(;a!=ae;++a){ T_terminal_def* tdef = a->second; @; @; using namespace NS_cweave_T_sdc; Ccweave_T_sdc sdc_fsm; XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),term_name); sdc_fsm.initialize(&Fsm->ow_err_file_,term_name); Parser sdc_emit(sdc_fsm,&dirs_tokens,0); sdc_emit.parse(); } Fsm->ow_err_file_ << "@@** Index.\n"; } @*2 |MPOST_CWEB_wrt_lrk|.\fbreak |cweave|'s |lrk| portion of grammar vocabulary. @*4 Output |lrk| macros and files. @= output_cweb_macros_and_banner( Fsm,Fsm->ow_lrk_file_,"Lr K Vocabulary",Fsm->gened_date_time_.c_str() ,Lrk_phrase->filename_id()->identifier() ,Lrk_phrase->namespace_id()->identifier() ,Lrk_phrase->alphabet()->size() ); @*5 Lrk hrule to end T entry.\fbreak @= KCHARP rule_it = "\\fbreak%s\n"@/ "\\hrule\n"; x = sprintf(Fsm->big_buf_,rule_it," "); Fsm->ow_lrk_file_.write(Fsm->big_buf_,x); @*4 Output |lrk|'s entry.\fbreak For now i hardwire the enumeration label here. It will be deposited in the enumeration grammar for me to fetch. Note: why the problem with |reduce operator|? The index part of pdftex honks when it uses the \TeX macro |reduce_operator| so i renamed it to |rrrr| to give the proper effect \rrrr. @= char ab[] = "Y"; if(tdef->autoabort() == true) ab[0] = 'Y'; else ab[0]= 'N'; char ad[] = "Y"; if(tdef->autodelete() == true) ad[0] = 'Y'; else ad[0] = 'N'; switch(tdef->enumerated_id__){ case T_Enum::T_LR1_parallel_operator_:{ strcpy(name,"\\paralleloperator"); strcpy(term_name,"|parallel_operator|"); break; } case T_Enum::T_LR1_all_shift_operator_:{ strcpy(name,"\\allshift"); strcpy(term_name,"|all_shift|"); break; } case T_Enum::T_LR1_invisible_shift_operator_:{ strcpy(name,"\\invisibleshift"); strcpy(term_name,"|invisible_shift|"); break; } case T_Enum::T_LR1_fset_transience_operator_:{ strcpy(name,"\\transienceoperator"); strcpy(term_name,"|transience_operator|"); break; } case T_Enum::T_LR1_questionable_shift_operator_:{ strcpy(name,"\\questionableoperator"); strcpy(term_name,"|questionable_operator|"); break; } default: { XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),name); XLATE_SYMBOLS_FOR_cweave(tdef->t_name()->c_str(),term_name); break; } } const char* enum_class_name_format = "T\\_%s\\_"; char class_name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(tdef->classsym()->c_str(),class_name); char a[SMALL_BUFFER_4K]; sprintf(a,enum_class_name_format,class_name); KCHARP t_entry = "@@*2 {\\bf %s}.\\fbreak\n"@/ "Enum: %s\n\\fbreak\n"@/ "\\line{Class: %s \\hfil AB: %s \\hfil AD: %s}\n"; int x = sprintf(Fsm->big_buf_ ,t_entry ,name ,a ,class_name ,ab ,ad ); Fsm->ow_lrk_file_.write(Fsm->big_buf_,x); Fsm->ow_lrk_file_<< endl; if(tdef->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_lrk_file_,tdef->cweb_marker()); } @; @*4 Output lrk suffix code if present.\fbreak @= if(Lrk_phrase->lrk_sufx_code() != 0){ KCHARP tsuf = "@@*1 {\\bf lrk-sufx} directive.\\fbreak\n"; Fsm->ow_lrk_file_ << tsuf; T_syntax_code* stc = Lrk_phrase->lrk_sufx_code(); if(stc->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_lrk_file_,stc->cweb_marker()); } KCHARP tsuf_code = "@@=\n" "%s" "\n"; int x = sprintf(Fsm->big_buf_ ,tsuf_code ,Lrk_phrase->lrk_sufx_code()->syntax_code()->c_str() ); Fsm->ow_lrk_file_.write(Fsm->big_buf_,x); Fsm->ow_lrk_file_<< endl; } @*3 Driver of |MPOST_CWEB_wrt_Lrk|. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_Lrk@/ (Cmpost_output* Fsm,T_lr1_k_phrase* Lrk_phrase){@/ @; if(Lrk_phrase->cweb_marker() != 0){ WRT_CWEB_MARKER(&Fsm->ow_lrk_file_,Lrk_phrase->cweb_marker()); } char term_name[Max_cweb_item_size]; char name[Max_cweb_item_size]; T_DEF_MAP_ITER_type a = Lrk_phrase->alphabet()->begin(); T_DEF_MAP_ITER_type ae = Lrk_phrase->alphabet()->end(); for(;a!=ae;++a){ T_terminal_def* tdef = a->second; @; @; using namespace NS_cweave_T_sdc; Ccweave_T_sdc sdc_fsm; sdc_fsm.initialize(&Fsm->ow_lrk_file_,term_name); Parser sdc_emit(sdc_fsm,&dirs_tokens,0); sdc_emit.parse(); } @; Fsm->ow_lrk_file_ << "@@** Index.\n"; } @*2 |MPOST_CWEB_wrt_rule_s_lhs_sdc|.\fbreak |cweave|'s statements for a rule's ``lhs'' syntax directed code. Go to it grammar. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_wrt_rule_s_lhs_sdc@/ (Cmpost_output* Fsm,rule_def* Rule_def){@/ T_rule_lhs_phrase* rlp = Rule_def->rule_lhs(); if(rlp == 0) return; SDC_MAP_type* sr_dirs = rlp->lhs_directives_map(); SDC_MAP_ITER_type i = sr_dirs->begin(); SDC_MAP_ITER_type ie = sr_dirs->end(); TOKEN_GAGGLE dirs_tokens; for(;i!=ie;++i){ CAbs_lr1_sym* sym = i->second; dirs_tokens.push_back(*sym); } T_parallel_monitor_phrase* ppm = Rule_def->parallel_mntr(); if(ppm != 0){ sr_dirs = ppm->mntr_directives_map(); i = sr_dirs->begin(); ie = sr_dirs->end(); for(;i!=ie;++i){ CAbs_lr1_sym* sym = i->second; dirs_tokens.push_back(*sym); } } using namespace NS_cweave_lhs_sdc; Ccweave_lhs_sdc lhs_sdc_fsm; lhs_sdc_fsm.initialize(&Fsm->ow_file_,Rule_def->rule_name()->c_str()); Parser lhs_sdc_emit(lhs_sdc_fsm,&dirs_tokens,0); lhs_sdc_emit.parse(); } @*2 |MPOST_CWEB_should_subrule_be_printed|.\fbreak Check whether the subrule should be |cweave|ed. If there is no syntax directed code, then bypass diagramming it as its rule already does the big picture --- forest. @+= bool NS_mpost_output:: Cmpost_output:: MPOST_CWEB_should_subrule_be_printed@/ (Cmpost_output* Fsm,T_subrule_def* Subrule_def){@/ if(Subrule_def->cweb_marker() != 0) return true; SDC_MAP_type* sr_dirs = Subrule_def->subrule_directives(); if(sr_dirs->empty() == true) return false; return true; } @*3 Deal with T's classifications. @!To do: emit single character references; adjust |cweave|?@>; @= switch(td->classification()){ case T_terminal_def::err:{ int x = sprintf(Fsm->big_buf_,xref,name); Fsm->ow_file_.write(Fsm->big_buf_,x); break; } case T_terminal_def::rc:{ break; INT_STR_MAP_ITER_type i = Fsm->xlated_names_.find(td->enumerated_id__); if(i != Fsm->xlated_names_.end()){ int x = sprintf(Fsm->big_buf_,xref,i->second.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); }else{ int x = sprintf(Fsm->big_buf_,xref,name); Fsm->ow_file_.write(Fsm->big_buf_,x); } break; } case T_terminal_def::lrk:{ INT_STR_MAP_ITER_type i = Fsm->xlated_names_.find(td->enumerated_id__); if(i != Fsm->xlated_names_.end()){ int x = sprintf(Fsm->big_buf_,xref,i->second.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); }else{ int x = sprintf(Fsm->big_buf_,xref,name); Fsm->ow_file_.write(Fsm->big_buf_,x); } break; } case T_terminal_def::t:{ INT_STR_MAP_ITER_type i = Fsm->xlated_names_.find(td->enumerated_id__); if(i != Fsm->xlated_names_.end()){ int x = sprintf(Fsm->big_buf_,xref,i->second.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); }else{ int x = sprintf(Fsm->big_buf_,xref,name); Fsm->ow_file_.write(Fsm->big_buf_,x); } break; } } @*2 |MPOST_CWEB_xref_refered_T|.\fbreak Don't emit single characters as |pdftex| honks in the index section as this was a decision to not clutter temporary variables. Until i get more intimate with its code i check to this limitation and my ignorance. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_xref_refered_T@/ (Cmpost_output* Fsm,refered_T* R_T){@/ T_in_stbl* t = R_T->t_in_stbl(); T_terminal_def* td = t->t_def(); KCHARP xref = "@@.%s@@>\n"; char name[Max_cweb_item_size]; XLATE_SYMBOLS_FOR_cweave(td->t_name()->c_str(),name); @; } @*2 |MPOST_CWEB_xref_refered_rule|.\fbreak Watch for underscore in rule's name. So makesure that |cweave|'s directive backslash underscore is part of the name. @+= void NS_mpost_output:: Cmpost_output:: MPOST_CWEB_xref_refered_rule@/ (Cmpost_output* Fsm,refered_rule* R_rule){@/ rule_in_stbl* r = R_rule->Rule_in_stbl(); rule_def* rd = r->r_def(); KCHARP xref = "@@.%s@@>\n"; string name_with_bkslash; int len = rd->rule_name()->length(); for(int x=0;xrule_name())[x]; if(c == '_'){ name_with_bkslash += '\\'; } name_with_bkslash += c; } int x = sprintf(Fsm->big_buf_,xref,name_with_bkslash.c_str()); Fsm->ow_file_.write(Fsm->big_buf_,x); } @** Grammar's ``Called threads'' First Set Calculation.\fbreak What is it? Similar to a terminal first set definition, it is the list of ``called thread'' expressions in the ``Start Rule'' arrived at from rule closures. Why is it needed? It is the content of the ``list-of-transitive-threads'' construct emitted in the grammar's ``fsc'' file for \olinker to resolve into T first sets per thread. These first sets are what determines whether a thread should be called. \fbreak \fbreak The Algorithm:\fbreak \INDENT{.1in}{add start rule's subrules to the element space} \INDENT{.1in}{walk the element space} \INDENT{.2in}{Element assessment:} \INDENT{.3in}{a) eosubrule: advance to next point in space} \INDENT{.3in}{b) referenced rule:} \INDENT{.4in}{determine if referenced rule already contributed} \INDENT{.5in}{no --- add rule to contributed closure rules set} \INDENT{.7in}{add its subrules to the element space} \INDENT{.4in}{is ``referenced rule'' epsilonable?} \INDENT{.5in}{yes --- overlay current element with its right neighbour} \INDENT{.5in}{no --- advance to next subrule in element space} \INDENT{.3in}{c) \PARshift rtn-T NSa::THa type expression?} \INDENT{.5in}{no --- advance to next subrule point in space} \INDENT{.5in}{yes --- add called thread to ``called thread first set'} \INDENT{.7in}{advance to next subrule in element space} \INDENT{.1in}{until all entries in element space stomped on} \fbreak The algorithm uses a 1 dimensional space of subrules. Each point represents a subrule's element position within its rhs. As the subrule's current element is being evaluated, if the next element in the subrule is needed due to the current element being an epsilonable rule it overlays the being assessed element. Effectively the subrule being processed is a fishing line that reels in its next element ontop of the current element. It tap dances ontop of the element point until the subrule's elements assessment is exhausted before advancing to the next subrule. After, the subrules residues are their last element that exhausted its assessment. The element space is fixed in size: just the ``total number of subrules''. \fbreak \settabs\+\indent&1in\qquad&\qquad&{\it subrule's symbols}&\qquad&\qquad&\cr \+&\it Rule&&{\it subrule's symbols}&&{residue sym}\cr \+&St &$\rightarrow$ &$ $ S $\bot$&&S\cr \+&S &$\rightarrow$ &$ $ $R_a^{\epsilon}$ $R_b^{\epsilon}$ $R_c$&&$R_c$\cr \+& &$\rightarrow$ &$ $ $R_a^{\epsilon}$ $R_d^{\epsilon}$ $R_e$&&$R_e$\cr \+&$R_a^{\epsilon}$ &$\rightarrow$ &&& \cr \+&&$\rightarrow$ &$ $ \PARshift $ a$ NSa::THa&&\PARshift\cr \+&$R_b^{\epsilon}$ &$\rightarrow$ &&& \cr \+&&$\rightarrow$ &$ $ \PARshift $ b$ NSb::THb&&\PARshift\cr \+&$R_c$ &$\rightarrow$ &$ $ c&&c\cr \+&$R_d^{\epsilon}$ &$\rightarrow$ &&& \cr \+&&$\rightarrow$ &$ $ \PARshift $ d$ NSd::THd&&\PARshift\cr \+&$R_e$ &$\rightarrow$ &$ $ e&&e\cr \fbreak \hbox{\hskip.5in Above grammar's called thread's first set: FS(Tha)+FS(Thb)+FS(THd)}\fbreak \hbox{\hskip.7in "/usr/local/yacco2/qa/test\_called\_thd\_fs.lex" grammar exercises algorithm}\fbreak @*4 Add subrule's 1st element to element space. @= vector::iterator ti = subrules_can.nodes_visited()->begin(); vector::iterator tie = subrules_can.nodes_visited()->end(); for(;ti!=tie;++ti){@/ AST* srdef_t = *ti; @=T_subrule_def* sr_def = (T_subrule_def*)AST::content(*srdef_t);@>@/ AST* sr_t = sr_def->subrule_s_tree(); AST* et = AST::get_spec_child(*sr_t,1); ++nc; elem_space[nc]=et; } @*3 Add Start rule's subrules to element space.\fbreak @= { AST* rtree = Start_rule->rule_s_tree(); ast_prefix_1forest subrule_walk(*rtree,&just_walk_functr,&sr_filter,ACCEPT_FILTER); tok_can subrules_can(subrule_walk); yacco2::UINT xxx(0); for(;subrules_can.operator[](xxx) != yacco2::PTR_LR1_eog__;++xxx) ; @; } @*3 Add referenced rule to element space.\fbreak @= { i = rules_in_elem_space.find(rdef); if(i == rules_in_elem_space.end()){// rule not dealt with so add it rules_in_elem_space.insert(rdef); AST* rtree = rdef->rule_s_tree(); ast_prefix_1forest subrule_walk(*rtree,&just_walk_functr,&sr_filter,ACCEPT_FILTER); tok_can subrules_can(subrule_walk); yacco2::UINT xxx(0); for (;subrules_can.operator[](xxx) != yacco2::PTR_LR1_eog__;++xxx) ; @; } } @*3 Assess element type.\fbreak Handles ``called thread'' expression. All other types are thrown out. @= switch(cur_elem->enumerated_id__){ case T_Enum::T_T_eosubrule_:{ ++cc;//finished with subrule, so adv to next subrule continue; } case T_Enum::T_refered_T_:{ @=refered_T* rt = (refered_T*)cur_elem;@>@/ T_terminal_def* td = rt->its_t_def(); if(td->enum_id() != LR1_PARALLEL_OPERATOR){ ++cc;//finished with subrule, so adv to next subrule continue; } @; ++cc;//finished with called expr, so adv to next subrule continue; } case T_Enum::T_refered_rule_:{ @=refered_rule* rr = (refered_rule*)cur_elem;@>@/ rule_def* rdef = rr->its_rule_def(); @; if(rdef->epsilon() == YES){ AST* et = AST::brother(*cur_elem_t); elem_space[cc]=et;//overlay continue; }else{// finished with subrule ++cc;//adv to next subrule continue; } } } @*4 Deal with ``thread call'' expression.\fbreak Get the ``called-thread-eosubrule'' terminal in the 3rd tree position. Only add it and not the ``null-called-thread-eosubrule'' as it indicates that one of the called threads will return its stated ``return T''. @= AST* first_el_t = cur_elem_t; AST* third_el_t = AST::get_younger_sibling(*first_el_t,2); CAbs_lr1_sym* sym = AST::content(*third_el_t); if(sym->enumerated_id__ == T_Enum::T_T_called_thread_eosubrule_){ T_called_thread_eosubrule* ct = (T_called_thread_eosubrule*)sym; Start_rule->add_to_called_thread_first_set(ct); } @*2 |GEN_CALLED_THREADS_FS_OF_RULE|.\fbreak Make |elem_space| local to the translation unit instead of the local to the function for efficiecy reasons. @+= AST* elem_space[Max_no_subrules]; void GEN_CALLED_THREADS_FS_OF_RULE(rule_def* Start_rule){@/ using namespace NS_yacco2_T_enum; using namespace std; std::set rules_in_elem_space; std::set::iterator i; INT_SET_type sr_filter; sr_filter.insert(T_Enum::T_T_subrule_def_); tok_can_ast_functor just_walk_functr; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; int cc(0); // cur cell int nc(-1); // next cell AST* cur_elem_t(0); CAbs_lr1_sym* cur_elem(0); @; while(cc <=nc){ cur_elem_t = elem_space[cc]; cur_elem = AST::content(*cur_elem_t); @; } } @** First set calculations.\fbreak Raison d'\^etre: ugly formatted code by cweave. This code should reside in |first_set_rules| grammar. There are 2 things being done. Calculate the first set and record the closure only rules for the state used to generate the follow sets. @*3 Add Defined rule's subrules to element space. @= { AST* rtree = Rule_def->rule_s_tree(); ast_prefix_1forest subrule_walk(*rtree,&just_walk_functr,&sr_filter,ACCEPT_FILTER); tok_can subrules_can(subrule_walk); yacco2::UINT xxx(0); for(;subrules_can.operator[](xxx) != yacco2::PTR_LR1_eog__;++xxx) ; @; } @*3 Assess element type. @= switch(cur_elem->enumerated_id__){ case T_Enum::T_T_eosubrule_:{ ++cc;//finished with subrule, so adv to next subrule continue; } case T_Enum::T_refered_T_:{ @=refered_T* rt = (refered_T*)cur_elem;@>@/ T_in_stbl* tstbl = rt->t_in_stbl(); Rule_def->add_to_first_set(*tstbl); Rule_def->add_rule_adding_T_in_first_set(tstbl,rt->grammar_s_enumerate()); ++cc;//finished subrule, so adv to next subrule continue; } case T_Enum::T_refered_rule_:{ @=refered_rule* rr = (refered_rule*)cur_elem;@>@/ rule_def* rdef = rr->its_rule_def(); @; if(rdef->epsilon() == YES){ AST* et = AST::brother(*cur_elem_t); elem_space[cc]=et;//overlay continue; }else{// finished with subrule ++cc;//adv to next subrule continue; } } } @*3 Check Start rule for \emptyrule{ }condition.\fbreak This deals with thread only grammars. Their first sets determine whether they get called. So why the check? What does it mean when the Start rule is \emptyrule{ }for a thread? The backstop is its lookahead that normally becomes the reducing T set to accept the grammar. I choose to call it just in case the thread is a logic sequencer of \emptyrule{ }rules. The consequence is just an abort blip by the called thread. If the grammar writer doesn't like it, remove the Start rule \emptyrule{ }condition. @= if(Rule_def->rule_no() != 1) goto not_start_rule; if(O2_PP_PHASE == 0) goto not_start_rule;// not a thread grammar if(Rule_def->epsilon() == NO) goto not_start_rule;// LA not needed { T_parallel_la_boundary* la = O2_PP_PHASE->la_bndry(); set::iterator lai = la->la_first_set()->begin(); set::iterator laie = la->la_first_set()->end(); for(;lai!=laie;++lai){ T_in_stbl* tstbl = *lai; Rule_def->add_to_first_set(*tstbl); } } not_start_rule:; @*2 |GEN_FS_OF_RULE|.\fbreak The only wrinkle is the Start rule. What do u do when the grammar is a thread and the Start rule is \emptyrule? Well add the ``lookahead expression'' to its first set. @+= void GEN_FS_OF_RULE(rule_def* Rule_def){@/ using namespace NS_yacco2_T_enum; using namespace std; std::set rules_in_elem_space; std::set::iterator i; INT_SET_type sr_filter; sr_filter.insert(T_Enum::T_T_subrule_def_); tok_can_ast_functor just_walk_functr; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; int cc(0); // cur cell int nc(-1); // next cell AST* cur_elem_t(0); CAbs_lr1_sym* cur_elem(0); @; @; while(cc <=nc){ cur_elem_t = elem_space[cc]; cur_elem = AST::content(*cur_elem_t); @; } } @** Print out Rule's First Set.\fbreak The rule's first set elements are sorted in lexicographical order. @*4 Set up first set sort.\fbreak Why your own copy into a ``random iterator'' container? Glad u asked dave, MS ``copy'' template algorithm aborts and ``stable\_sort'' requires a ``random iterator'' or it will give u a compile time error on non Microsoft platforms. |copy(fs->begin(),fs->end(),ran_array.begin());| works properly on Sun. @= T_IN_STBL_SET_type* fs = Rule_def->first_set(); Ofile << "@@*2 |" << Rule_def->rule_name()->c_str() << "|"; if(Rule_def->epsilon() == true){ Ofile << "{$^\\emptyrule$}"; } Ofile << " \\# in set: " << fs->size() << ".\\fbreak"<< std::endl; T_IN_STBL_SORTED_SET_type ran_array; ran_array.reserve(fs->size()); T_IN_STBL_SET_ITER_type ii= fs->begin(); T_IN_STBL_SET_ITER_type iie= fs->end(); for(;ii != iie;++ii){ ran_array.push_back(*ii); } stable_sort(ran_array.begin(),ran_array.end(),fs_sort_criteria); @*3 Print out the sorted first set elements. @= char xlate[Max_cweb_item_size]; T_IN_STBL_SORTED_SET_ITER_type i = ran_array.begin(); T_IN_STBL_SORTED_SET_ITER_type ie = ran_array.end(); for(;i!=ie;++i){ T_in_stbl* el = *i; Ofile<<" "; XLATE_SYMBOLS_FOR_cweave(el->t_def()->t_name()->c_str(),xlate); Ofile << xlate <<"\\ \\ "<+= bool fs_sort_criteria(const NS_yacco2_terminals::T_in_stbl*E1,const NS_yacco2_terminals::T_in_stbl*E2){ @=T_in_stbl* e1 = (T_in_stbl*)E1;@>@/ @=T_in_stbl* e2 = (T_in_stbl*)E2;@>@/ T_terminal_def* e1_t_def = e1->t_def(); T_terminal_def* e2_t_def = e2->t_def(); string* e1s = e1_t_def->t_name(); string* e2s = e2_t_def->t_name(); if(*e1s < *e2s)return true; return false; } void PRT_RULE_S_FIRST_SET(std::ofstream& Ofile,NS_yacco2_terminals::rule_def* Rule_def){ @; @; } @** Commonize LA sets.\fbreak @*3 |find_common_la_set_idx|.\fbreak Reduce the number of lookahead sets by keeping a global registry. The index returned is the vector's index. This value is deposited in the state element's reducing configuration. It is is used in manufacturing the common la set's name.\fbreak \fbreak Some optimizations:\fbreak If |eolr| is in set, as it represents all terminals, just keep it as the other terminals are ch ch chatter. @+= LA_SET_type* make_copy_of_la_set(LA_SET_type* Set1){ LA_SET_type* copy_set = new LA_SET_type; using namespace yacco2_stbl; T_sym_tbl_report_card report_card; find_sym_in_stbl(report_card,*LR1_EOLR_LITERAL); @=T_in_stbl* td = (T_in_stbl*)report_card.tbl_entry_->symbol_;@>@/ LA_SET_ITER_type f = Set1->find(td); if(f != Set1->end()){ copy_set->insert(td); return copy_set; } LA_SET_ITER_type i = Set1->begin(); LA_SET_ITER_type ie = Set1->end(); for(;i!=ie;++i){ copy_set->insert(*i); } return copy_set; } @*3 |are_2_la_sets_equal|.\fbreak See bugs as to why i roll my own. @+= bool are_2_la_sets_equal(LA_SET_type* Common_set1,LA_SET_type* Set2){ LA_SET_type* la_set_to_compare_against = Set2; using namespace yacco2_stbl; T_sym_tbl_report_card report_card; find_sym_in_stbl(report_card,*LR1_EOLR_LITERAL); @=T_in_stbl* td = (T_in_stbl*)report_card.tbl_entry_->symbol_;@>@/ LA_SET_type* eolr_set(0); LA_SET_ITER_type f = Set2->find(td); if(f != Set2->end()){ eolr_set = new LA_SET_type; eolr_set->insert(td); la_set_to_compare_against = eolr_set; } if(Common_set1->size() != la_set_to_compare_against->size()){ if(eolr_set !=0) delete eolr_set; return false; } LA_SET_ITER_type set1i = Common_set1->begin(); LA_SET_ITER_type set1ie = Common_set1->end(); LA_SET_ITER_type set2i = la_set_to_compare_against->begin(); LA_SET_ITER_type set2ie = la_set_to_compare_against->end(); for(;set1i != set1ie;){ T_in_stbl* set1T = *set1i; T_in_stbl* set2T = *set2i; if(set1T != set2T) return false; ++set1i; ++set2i; } if(eolr_set != 0) delete eolr_set; return true; } @*3 |find_common_la_set_idx|.\fbreak @+= int find_common_la_set_idx(LA_SET_type* La_){ if(COMMON_LA_SETS.empty() == true){ LA_SET_type* copy_set = make_copy_of_la_set(La_); COMMON_LA_SETS.push_back(copy_set); return 0; } COMMON_LA_SETS_ITER_type i = COMMON_LA_SETS.begin(); COMMON_LA_SETS_ITER_type ie = COMMON_LA_SETS.end(); int idx(0); for(;i!=ie;++i,++idx){ LA_SET_type* st1 = *i; bool result = are_2_la_sets_equal(st1,La_); if(result == true){ return idx; } } LA_SET_type* copy_set = make_copy_of_la_set(La_); COMMON_LA_SETS.push_back(copy_set); return idx; } @*2 |COMMONIZE_LA_SETS|.\fbreak Reduce the number of lookahead sets by keeping a global registry. The index returned is the COMMON\_LA\_SETS index. This value is deposited in the state element's reducing configuration. It is used in the manufacturing of the common la set's name referenced from the emitted tables. @+= void COMMONIZE_LA_SETS(){ STATES_ITER_type si = LR1_STATES.begin(); STATES_ITER_type sie = LR1_STATES.end(); for(;si!=sie;++si){ state* cur_state = *si; S_VECTORS_ITER_type svi = cur_state->state_s_vector_.begin(); S_VECTORS_ITER_type svie = cur_state->state_s_vector_.end(); 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){ state_element* se = *seli; if(se->goto_state_ != 0) continue; bool set_ok = se->calc_la(*se);// make sure it is filled directly if (set_ok == false){// la set is empty so get out return; } se->common_la_set_idx_ = find_common_la_set_idx(se->la_set_); } } } } @** Calculate recycling rule's use count.\fbreak This is external as my cweave documentation approach to grammars is rudimentary. It tag teams with |rules_use_cnt.lex| grammar who drives the overall evaluation and use count posting per defined rule.\fbreak \INDENT{.1in}{1) if RxSkeleton exists in cyclic table then return its use value} \INDENT{.1in}{2) add RxSkeleton to cyclic table} \INDENT{.1in}{3) evaluate each skeleton's rhs for rule's use cnt} \INDENT{.2in}{a) determine indirect use count for rhs} \INDENT{.2in}{b) determine rhs's max use cnt from direct and indirect use cnts} @*3 |calc_cyclic_key|.\fbreak Compound keys for a c++ map template are a nuisance due to the support operators required for key ordering etc. So i'll use a unique key of int and let the map deal well with an integer key. The 2 parts of the key are the rule defs using their enumerated values. So i'll make the 2 parts of the key as a number system of base ``number of rules''. Part 1 is the 2nd digit position and part 2 is the units digit. As the rule's enumeration starts after the T vocabulary, i must remove the relative ranking of the T vocabulary. This way i'm only dealing with base number of rules. @+= int calc_cyclic_key@/ (NS_yacco2_terminals::rule_def* Rule_use,NS_yacco2_terminals::rule_def* Against_rule){@/ using namespace NS_yacco2_T_enum; static int no_of_rules(0); static int start_of_rules(0); if(no_of_rules == 0){ start_of_rules = O2_T_ENUM_PHASE->total_enumerate(); no_of_rules = O2_RULES_PHASE->rules_alphabet()->size(); } int rule_use_enum_rel0 = Rule_use->enum_id() - start_of_rules; int part1_key_of_rule_use = rule_use_enum_rel0 * no_of_rules; int against_rule_enum_rel0 = Against_rule->enum_id() - start_of_rules; int part2_key_of_against_rule = against_rule_enum_rel0; int key = part1_key_of_rule_use + part2_key_of_against_rule; return key; } @*3 |determine_rhs_indirect_use_cnt|.\fbreak The subrule forest is passed in. Just walk its rhs's elements. Indirect count comes from rules who reference the ``use cnt'' rule. The assessment is from its maximum derives count determined against all its rhses. Note the recursion on a referenced rule within its rhs that can derive another rule that can reference the ``use cnt'' rule. @+= void determine_rhs_indirect_use_cnt@/ (NS_yacco2_terminals::rule_def* Use_for_rule,yacco2::AST* SRule_t ,NS_yacco2_terminals::rule_def* Against_rule){@/ using namespace NS_yacco2_T_enum; SET_FILTER_type filter; filter.insert(T_Enum::T_refered_rule_); tok_can_ast_functor walk_functr; ast_prefix_1forest walk(*SRule_t,&walk_functr,&filter,ACCEPT_FILTER); TOK_CAN_TREE_type rules_use_can(walk); int indirect_use_cnt(0); int direct_use_cnt(0); int x(0); CAbs_lr1_sym* sym=rules_use_can[x]; for(;sym->enumerated_id() != LR1_Eog;++x,sym=rules_use_can[x]){ @=refered_rule* rr = (refered_rule*)sym;@>@/ rule_def* its_rd = rr->its_rule_def(); if(its_rd == Use_for_rule){ continue;// direct cnt } int idc = MAX_USE_CNT_RxR(Use_for_rule,its_rd); } } @*3 |determine_rhs_max_use_cnt|.\fbreak The interesting part is to determine the the use count from direct and derived rules. A derived rule in the rhs must exercise its rhs elements before it reduces. This is like a lookahead of ``used rules''. Once it reduces to self, it frees up its ``used rules''. @+= int determine_rhs_max_use_cnt@/ (NS_yacco2_terminals::rule_def* Use_for_rule,yacco2::AST* SRule_t ,NS_yacco2_terminals::rule_def* Against_rule){@/ using namespace NS_yacco2_T_enum; SET_FILTER_type filter; filter.insert(T_Enum::T_refered_rule_); tok_can_ast_functor walk_functr; ast_prefix_1forest walk(*SRule_t,&walk_functr,&filter,ACCEPT_FILTER); TOK_CAN_TREE_type rules_use_can(walk); int running_indirect_use_cnt(0); int running_direct_use_cnt(0); int x(0); CAbs_lr1_sym* sym=rules_use_can[x]; rule_def* its_rd(0); for(;sym->enumerated_id() != LR1_Eog;++x,sym=rules_use_can[x]){ @=refered_rule* rr = (refered_rule*)sym;@>@/ its_rd = rr->its_rule_def(); if(its_rd == Against_rule) Against_rule->recursive(YES); if(its_rd == Use_for_rule){ ++running_direct_use_cnt; continue;// direct cnt }else{ use_cnt_type rule_use_key(Use_for_rule,its_rd); int use_cnt_key = calc_cyclic_key(Use_for_rule,its_rd); CYCLIC_USE_TBL_ITER_type i = CYCLIC_USE_TABLE.find(use_cnt_key); use_cnt_type& uc = i->second; int idc = uc.use_cnt_;// use cnt int potential_idc_cnt = idc + running_direct_use_cnt; if(potential_idc_cnt > running_indirect_use_cnt){ running_indirect_use_cnt = potential_idc_cnt; } } } if(running_direct_use_cnt > running_indirect_use_cnt){ return running_direct_use_cnt; } return running_indirect_use_cnt; } @*2 |MAX_USE_CNT_RxR|: called by |rules_use_cnt.lex| grammar.\fbreak Watch for cycling. It is initially called from |rules_use_cnt.lex| grammar. @+= int MAX_USE_CNT_RxR@/ (NS_yacco2_terminals::rule_def* Rule_use,NS_yacco2_terminals::rule_def* Against_rule){ use_cnt_type rule_use_key(Rule_use,Against_rule); int use_cnt_key = calc_cyclic_key(Rule_use,Against_rule); CYCLIC_USE_TBL_ITER_type i = CYCLIC_USE_TABLE.find(use_cnt_key); if(i != CYCLIC_USE_TABLE.end()){ return i->second.use_cnt_; } CYCLIC_USE_TABLE.insert(make_pair(use_cnt_key,rule_use_key)); using namespace NS_yacco2_T_enum; if(Against_rule->rule_use_skeleton() == 0){// no rules used in its rhses i = CYCLIC_USE_TABLE.find(use_cnt_key); use_cnt_type& uc = i->second; uc.use_cnt_ = 0; return 0; } SET_FILTER_type filter; filter.insert(T_Enum::T_T_subrule_def_); tok_can_ast_functor walk_functr; ast_prefix walk(*Against_rule->rule_use_skeleton(),&walk_functr ,&filter,ACCEPT_FILTER); TOK_CAN_TREE_type rules_use_can(walk); int use_cnt(0); int x(0); CAbs_lr1_sym* sym=rules_use_can[x]; for(;sym->enumerated_id() != LR1_Eog;++x,sym=rules_use_can[x]){// walk rhses AST* sr_t = rules_use_can.ast(x); determine_rhs_indirect_use_cnt(Rule_use,sr_t,Against_rule); int rhs_uc = determine_rhs_max_use_cnt(Rule_use,sr_t,Against_rule); if(use_cnt < rhs_uc){ use_cnt = rhs_uc; } } i = CYCLIC_USE_TABLE.find(use_cnt_key); use_cnt_type& uc = i->second; if((Against_rule->recursive() == YES) // eliminate self recursion && (Rule_use != Against_rule)) use_cnt = 2*use_cnt; uc.use_cnt_ = use_cnt; return use_cnt; } @** Emit Grammar by External procedures.\fbreak I choose this route to make life easy for me. A table of contents outlines the activities. Each specificly outputted component will be done by an external routine. Some calls could be nested within other external procedures. Local scope uses lower case letters for procedure names whilst global scope uses capital letters.\fbreak This approach isolates the variable scoping per routine where software responsibilities are self contained per module and not distributed! It would be nice if c++ could have nested procedure definitions but... \fbreak \fbreak State name convention:\fbreak fsm class name both for monolithic and thread grammars. An example:\fbreak Format: Sxxx\_yyy where xxx is the state number starting from 1 and yyy is the fsm class name : Ceol.\fbreak S1\_Ceol is the starting state for the Ceol grammar.\fbreak \fbreak Thread convention:\fbreak There are 2 thread name contexts --- the thread name to call and the thread entry reference:\fbreak Thread name procedure:\fbreak Name supplied by the ``parallel-thread-function'' construct.\fbreak Eample of a thread procedure from the ``eol.lex'' grammar:\fbreak yacco2::THR \_YACCO2\_CALL\_TYPE NS\_eol::TH\_eol(yacco2::Parser* To\_judge);\fbreak Notice that it is caccooned by its namespace supplied by its ``parallel-parser'' grammar contruct. \fbreak \fbreak \INDENT{.3in}{parallel-parser} \INDENT{.3in}{(} \INDENT{.4in}{parallel-thread-function} \INDENT{.5in}{TH\_angled\_string} \INDENT{.4in}{***} \INDENT{.4in}{parallel-la-boundary} \INDENT{.5in}{eolr} \INDENT{.4in}{***} \INDENT{.3in}{)} \fbreak \fbreak Thread entry Format: Iyyy where yyy is the thread entry name gened from \O2{}linker: Ixxx where xxx is thread name supplied by the ``parallel-thread-function'' construct.\fbreak Ex: ITH\_eol where eol grammar has ``parallel-thread-function'' as TH\_eol \fbreak \fbreak \fbreak Thread dispatch tables:\fbreak These are arrays sprinkled thru out the LR1 states. They provide the list of thread entries within the state of the threads to be dispatched. Their referenced names use the thread entry ``ITH\_eol'' format. \fbreak Within the lr1 state, any called threads will contain a dispatch array of thread procedure addresses. \fbreak \fbreak Arbitrators name convention:\fbreak Format: AR\_yyy where yyy is the rule name containing thread use. A lr state calling threads could have an arbitrator. This depends whether a rule that closured the called threads into the state has c++ code inside the ``arbitrator-code'' contruct. This contruct can be empty signifying that the called threads are deterministic: possibly only one of the called threads will return a terminal and no arbritation required to choose between 2 or more returned terminals. \fbreak \fbreak @* Template of Grammar's header file declaration: |OP_GRAMMAR_HEADER|.\fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) Includes of \O2 library and grammar vocabulary} \INDENT{.1in}{3) User-prefix-declaration directive from grammar} \INDENT{.1in}{4) grammar include guard declaration} \INDENT{.2in}{4.1) namespace declaration of grammar} \INDENT{.3in}{4.1.0) fsm's rules reuse table extern} \INDENT{.3in}{4.1.1) Possible thread entry extern if it's thread grammar} \INDENT{.3in}{4.1.2) fsm's 1st state extern reference} \INDENT{.3in}{4.1.3) Using \O2 and enumerate namespaces} \INDENT{.3in}{4.1.4) Fsm class declaration} \INDENT{.4in}{4.1.4.1) Enumeration of grammar's rules and subrules} \INDENT{.4in}{4.1.4.2) comments about number of la sets and states} \INDENT{.4in}{4.1.4.3) ctor of fsm --- constructor directive is implementation only} \INDENT{.4in}{4.1.4.4) dtor of fsm --- destructor directive is implementation only} \INDENT{.4in}{4.1.4.5) op and failed directives --- directives are implementation only} \INDENT{.4in}{4.1.4.6) user-declaration} \INDENT{.4in}{4.1.4.7) Create subrules to rules mapping} \INDENT{.3in}{4.1.5) grammar's rules declarations for forward references} \INDENT{.3in}{4.1.6) grammar's rules class definitions} \INDENT{.1in}{5) User-suffix-declaration directive from grammar} @*3 |intro_comment| --- Intro comments for emitted files.\fbreak A fixed format C++ comment describing the outputted file name with date / time stamp occurance. Used for all grammar's emitted files:\fbreak \INDENT{.2in}{1) xxx.h fsm header file with rule declarations} \INDENT{.2in}{2) xxx.cpp fsm class and rules implementation} \INDENT{.2in}{3) xxxsym.cpp reduce\_rhs\_of\_rule procedure and perchance to thread imps} \INDENT{.2in}{4) xxxtbl.cpp finite automaton --- the lr1 state network implementation} \fbreak Ip:\fbreak \INDENT{.4in}{1) output string} \INDENT{.4in}{2) file name suffix : 1 of .h, .cpp, sym.cpp, tbl.cpp} @+= void intro_comment (std::ofstream& Op_str,const char* File_name){@/ char big_buf_[BIG_BUFFER_32K]; KCHARP op_template = "/*\n"@/ " File: %s\n"@/ " Date and Time: %s\n"@/ "*/"; int x = sprintf(big_buf_ ,op_template ,File_name ,DATE_AND_TIME() ); Op_str.write(big_buf_,x); Op_str<+= void grammar_header_includes (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lrk_ph = O2_LRK_PHASE; T_rc_phrase* rc_ph = O2_RC_PHASE; T_error_symbols_phrase* err_ph = O2_ERROR_PHASE; T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_list = "#include \"%s\"\n" // yacco2 library HWired "#include \"%s.h\"\n" // enumeration "#include \"%s.h\"\n" // lrk symbols vocabulary "#include \"%s.h\"\n" // Errors vocabulary "#include \"%s.h\"\n" // Terminal vocabulary "#include \"%s.h\""; // raw characters vocabulary int x = sprintf(big_buf_@/ ,include_list@/ ,O2_library_file@/ ,enum_ph->filename_id()->identifier()->c_str()@/ ,lrk_ph->filename_id()->identifier()->c_str()@/ ,err_ph->filename_id()->identifier()->c_str()@/ ,t_ph->filename_id()->identifier()->c_str()@/ ,rc_ph->filename_id()->identifier()->c_str()@/ ); Op_str.write(big_buf_,x); Op_str<+= void user_prefix_declaration_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP upd_code = "%s"; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); SDC_MAP_type* dir_map = fsm_class->directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_user_prefix_declaration); if(i == dir_map->end()) return; @=T_user_prefix_declaration* upd = (T_user_prefix_declaration*)i->second;@>@/ int x = sprintf(big_buf_ ,upd_code ,upd->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void thread_entry_extern_for_header (std::ofstream& Op_str){@/ T_parallel_parser_phrase* pp_ph = O2_PP_PHASE; if(pp_ph == 0){ Op_str << "// monolithic grammar: no thread" << endl; return; } char big_buf_[BIG_BUFFER_32K]; KCHARP th_extern = "extern yacco2::Thread_entry I%s;\n"; int x = sprintf(big_buf_ ,th_extern ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void using_ns_for_header (std::ofstream& Op_str){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP default_ns_use = @/ "using namespace %s;// enumerate\n"@/ "using namespace yacco2;"; int x = sprintf(big_buf_ ,default_ns_use ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void fsm_enum_rules_subrules_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP enumeration_of_rules_sbrules = @/ " enum rules_and_subrules{\n"@/ " start_of_rule_list = %s::T_Enum::sum_total_T"; int x = sprintf(big_buf_ ,enumeration_of_rules_sbrules ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); int overall_subrules_nos(0); int current_rule_no(-1);// rel to zero int no_rules = rules_ph->rules_alphabet()->size(); KCHARP rule_enum = @/ " ,R_%s_ = %i//start_of_rule_list + %i"; for(;i!=ie;++i){ ++current_rule_no; rule_def* rd = *i; x = sprintf(big_buf_ ,rule_enum ,rd->rule_name()->c_str() ,rd->enum_id() ,current_rule_no++ ); Op_str.write(big_buf_,x); Op_str<subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); KCHARP subrule_enum = @/ " ,rhs%i_%s_ = %i"; for(;j != je;++j){ T_subrule_def* srd = *j; ++overall_subrules_nos; int x = sprintf(big_buf_ ,subrule_enum ,srd->subrule_no_of_rule() ,rd->rule_name()->c_str() ,overall_subrules_nos ); Op_str.write(big_buf_,x); Op_str<+= void fsm_map_subrules_to_rules_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP array_of_subrules_to_rules_mapping = @/ " fsm_rules_reuse_table_type fsm_rules_reuse_table;\n" " static int rhs_to_rules_mapping_[%i];"; T_rules_phrase* rules_ph = O2_RULES_PHASE; // loop thru the rules RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); int overall_subrules_nos(0); for(;i!=ie;++i){ rule_def* rd = *i; T_subrules_phrase* sr_ph = rd->subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); for(;j != je;++j){ ++overall_subrules_nos; } } ++overall_subrules_nos;// extra subrule due to [0] needed int x = sprintf(big_buf_ ,array_of_subrules_to_rules_mapping ,overall_subrules_nos ); Op_str.write(big_buf_,x); Op_str<+= void fsm_comments_about_la_sets_and_states (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP no_of_la_sets_and_states = @/ " //no of la sets = %i\n"@/ " //no of states = %i"; int x = sprintf(big_buf_ ,no_of_la_sets_and_states ,COMMON_LA_SETS.size() ,NO_LR1_STATES ); Op_str.write(big_buf_,x); Op_str<+= void fsm_class_declaration_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP class_fsm = @/ "class %s: public yacco2::CAbs_fsm {\n"@/ " public:"; int x = sprintf(big_buf_ ,class_fsm ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_user_declaration); if(i != dir_map->end()){ @=T_user_declaration* ud = (T_user_declaration*)i->second;@>@/ int x = sprintf(big_buf_ ,user_declaration ,ud->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void forward_refs_of_rules_declarations_for_header (std::ofstream& Op_str){@/ T_rules_phrase* rules_ph = O2_RULES_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP forward_ref = @/ "struct %s;"; // loop thru the rules RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); for(;i!=ie;++i){ rule_def* rd = *i; int x = sprintf(big_buf_ ,forward_ref ,rd->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void rules_class_defs_for_header (std::ofstream& Op_str){@/ T_rules_phrase* rules_ph = O2_RULES_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP end_rule_def = @/ "};\n"; @; } @ @= RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); KCHARP start_rule_def = @/ "struct %s:public yacco2::CAbs_lr1_sym {\n"@/ " %s(yacco2::Parser* P);"; for(;i!=ie;++i){ rule_def* rd = *i; @; int x = sprintf(big_buf_ ,start_rule_def ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<rule_lhs(); if(rlhs != 0){ SDC_MAP_type* dir_map = rlhs->lhs_directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_destructor); if(i != dir_map->end()){ KCHARP rule_def_dtor = @/ " static void dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P);"; int x = sprintf(big_buf_ ,rule_def_dtor ,rd->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<find(SDC_op); if(i != dir_map->end()){ KCHARP rule_def_op = @/ " void op();"; Op_str << rule_def_op<find(SDC_constructor); if(i != dir_map->end()){ KCHARP rule_def_ctor = @/ " void ctor();"; Op_str << rule_def_ctor<find(SDC_user_declaration); if(i != dir_map->end()){ @=T_user_declaration* ud = (T_user_declaration*)i->second;@>@/ Op_str << ud->syntax_code()->syntax_code()->c_str() <; } @*3 Gen rule's arbitrator procedure declaration.\fbreak It is declared outside of the rule making its call easier from the generated lr1 tables. @= if(rd->parallel_mntr() != 0){ T_parallel_monitor_phrase* pp_phrase = rd->parallel_mntr(); if(rd->parallel_mntr()->mntr_directives_map()->empty() != true){ SDC_MAP_type* dir_map = pp_phrase->mntr_directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_arbitrator_code); if(i !=dir_map->end()){ @=T_arbitrator_code* ac = (T_arbitrator_code*)i->second;@>@/ string::size_type idx = ac->syntax_code()->syntax_code()->find(CODE_PRESENCE_IN_ARBITRATOR_CODE); if(idx != string::npos){ KCHARP rule_s_arbitrator =@/ "yacco2::THR _YACCO2_CALL_TYPE\n"@/ "AR_%s(yacco2::Parser* Caller_pp);// rule's arbitrator"; int x = sprintf(big_buf_ ,rule_s_arbitrator ,rd->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<= KCHARP sub_rule_def_public = @/ " public:"; Op_str << sub_rule_def_public<subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); for(int xx = 1;j != je;++j,++xx){ T_subrule_def* srd = *j; SDC_MAP_type* sdrmap = srd->subrule_directives(); if(!sdrmap->empty()){ // any syntax directed code, call it int x = sprintf(big_buf_ ,sub_rule_def ,xx ); Op_str.write(big_buf_,x); Op_str<+= void possible_thread_procedure_declaration (std::ofstream& Op_str){@/ if(O2_PP_PHASE == 0)return; T_parallel_parser_phrase* pp_ph = O2_PP_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP thd_proc = @/ "yacco2::THR _YACCO2_CALL_TYPE\n"@/ "%s(yacco2::Parser* Caller);// called thread\n" "yacco2::THR_result _YACCO2_CALL_TYPE\n"@/ "PROC_%s(yacco2::Parser* Caller);// called thread's twin the procedure"; int x = sprintf(big_buf_ ,thd_proc ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void rules_reuse_table_declaration (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_rules_phrase* rules_ph = O2_RULES_PHASE; int no_rules = rules_ph->rules_alphabet()->size(); char big_buf_[BIG_BUFFER_32K]; KCHARP rules_recycled_table_type = @/ "struct fsm_rules_reuse_table_type{\n"@/ " fsm_rules_reuse_table_type();\n"@/ " int no_rules_entries_;\n"@/ " Per_rule_s_reuse_table* per_rule_s_table_[%i];\n"@/ "};"; int x = sprintf(big_buf_ ,rules_recycled_table_type ,no_rules ); Op_str.write(big_buf_,x); Op_str<+= void namespace_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP ns_of_grammar_start = @/ "namespace %s {"; int x = sprintf(big_buf_ ,ns_of_grammar_start ,fsm_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void state_1_extern (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; KCHARP state_extern = @/ "extern yacco2::State S1_%s;"; int x = sprintf(big_buf_ ,state_extern ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void grammar_include_guard_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; KCHARP signal_guard_start = @/ "#ifndef __%s_h__\n"@/ "#define __%s_h__ 1"; int x = sprintf(big_buf_ ,signal_guard_start ,fsm_ph->filename_id()->identifier()->c_str() ,fsm_ph->filename_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_suffix_declaration_for_header (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; char big_buf_[BIG_BUFFER_32K]; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); SDC_MAP_type* dir_map = fsm_class->directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_user_suffix_declaration); if(i == dir_map->end()) return; @=T_user_suffix_declaration* upd = (T_user_suffix_declaration*)i->second;@>@/ KCHARP upd_code = "%s"; int x = sprintf(big_buf_ ,upd_code ,upd->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void OP_GRAMMAR_HEADER(TOKEN_GAGGLE& Error_queue){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; string fn(fsm_ph->filename_id()->identifier()->c_str()); fn += Suffix_fsmheader; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_fsmheader_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*fsm_ph->filename_id()); sym->set_who_created("o2externs.w - OP_GRAMMAR_HEADER",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); grammar_include_guard_for_header(Op_file); user_suffix_declaration_for_header(Op_file); Op_file.close(); } @* Template of Grammar's fsm file implementation: |OP_GRAMMAR_CPP|.\fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) Include of grammar's header} \INDENT{.1in}{3) using namespaces} \INDENT{.1in}{4) fill in rules reuse table} \INDENT{.1in}{5) Fsm class implementation} \INDENT{.1in}{6) grammar's rules / subrules implementations} @*3 |fsm_cpp_includes|.\fbreak Get grammar's include file. @+= void fsm_cpp_includes (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_list = "#include \"%s.h\""; // grammar's header int x = sprintf(big_buf_ ,include_list ,fsm_ph->filename_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void using_ns_for_fsm_cpp (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lrk_ph = O2_LRK_PHASE; T_rc_phrase* rc_ph = O2_RC_PHASE; T_error_symbols_phrase* err_ph = O2_ERROR_PHASE; T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP default_ns_use = @/ "using namespace %s;// enumerate\n"@/ "using namespace %s;// error symbols\n"@/ "using namespace %s;// lrk \n"@/ "using namespace %s;// terminals\n"@/ "using namespace %s;// rc \n"@/ "using namespace %s;// yacco2 library\n"@/ "using namespace %s;// grammar's ns\n"@/ "// first set terminals"; int x = sprintf(big_buf_ ,default_ns_use ,enum_ph->namespace_id()->identifier()->c_str() ,err_ph->namespace_id()->identifier()->c_str()@/ ,lrk_ph->namespace_id()->identifier()->c_str()@/ ,t_ph->namespace_id()->identifier()->c_str()@/ ,rc_ph->namespace_id()->identifier()->c_str()@/ ,"yacco2"@/ ,fsm_ph->namespace_id()->identifier()->c_str()@/ ); Op_str.write(big_buf_,x); Op_str<+= void rules_reuse_table_implementation (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_rules_phrase* rules_ph = O2_RULES_PHASE; int no_rules= rules_ph->rules_alphabet()->size(); char big_buf_[BIG_BUFFER_32K]; KCHARP rules_recycled_table_type = @/ "fsm_rules_reuse_table_type::fsm_rules_reuse_table_type(){\n" " no_rules_entries_ = %i;"; int x = sprintf(big_buf_ ,rules_recycled_table_type ,no_rules ); Op_str.write(big_buf_,x); Op_str<+= void fsm_map_subrules_to_rules_table_imp (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_rules_phrase* rules_ph = O2_RULES_PHASE; char big_buf_[BIG_BUFFER_32K]; RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); int overall_subrules_nos(0); for(;i!=ie;++i){ rule_def* rd = *i; T_subrules_phrase* sr_ph = rd->subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); for(;j != je;++j){ ++overall_subrules_nos; } } ++overall_subrules_nos;// extra subrule due to [0] needed KCHARP array_of_subrules_to_rules_mapping_start = @/ "int %s::rhs_to_rules_mapping_[%i] = {\n"@/ " -1";// make room for false [0] entry int x = sprintf(big_buf_ ,array_of_subrules_to_rules_mapping_start ,fsm_class->identifier()->identifier()->c_str() ,overall_subrules_nos ); Op_str.write(big_buf_,x); Op_str<crt_order()->begin(); ie = rules_ph->crt_order()->end(); overall_subrules_nos = 0; for(;i!=ie;++i){ rule_def* rd = *i; T_subrules_phrase* sr_ph = rd->subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); for(;j != je;++j){ ++overall_subrules_nos; int x = sprintf(big_buf_ ,subrules_to_rule_mapping ,rd->rule_no()-1 ,overall_subrules_nos ,rd->rule_no() ); Op_str.write(big_buf_,x); Op_str<total_no_subrules(overall_subrules_nos); } @*3 |fsm_class_implementation|.\fbreak Directives: --- see |fsm_class_phrase_th| grammar\fbreak \INDENT{.3in}{1) constructor} \INDENT{.3in}{2) destructor} \INDENT{.3in}{3) failed} \INDENT{.3in}{4) op} \INDENT{.3in}{5) load up subrles mapping into rules} \INDENT{.3in}{6) user-implementation roll your own code if present} \INDENT{.3in}{7) user-imp-tbl --- injection code for xxxtbl.cpp where xxx is grammar name} \INDENT{.3in}{8) user-imp-sym --- injection code for xxxsym.cpp where xxx is grammar name} \fbreak Points 6 and 7 will be dealt with in their own ``cpp'' implementations. Note: added deletion of each ``recycling table'' per rule within the dtor. @+= void fsm_class_implementation (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP ctor_fsm = @/ " %s::\n"@/ " %s()\n"@/ " :yacco2::CAbs_fsm\n"@/ " (\"%s\"\n"@/ " ,\"%s\"\n"@/ " ,\"%s\"\n"@/ " ,%s\n"@/ " ,\"%s\"\n"@/ " ,\"%s\"\n"@/ " ,S1_%s){\n"@/ " %s\n"@/ " }"; SDC_MAP_type* dir_map = fsm_class->directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_constructor); const char* cc_str(0); if(i != dir_map->end()){ @=T_constructor* cc = (T_constructor*)i->second;@>@/ cc_str = cc->syntax_code()->syntax_code()->c_str(); }else{ cc_str = ""; } int x = sprintf(big_buf_ ,ctor_fsm ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_ph->fsm_id()->c_string()->c_str() ,fsm_ph->version()->c_string()->c_str() ,fsm_ph->date()->c_string()->c_str() ,fsm_ph->debug()->c_string()->c_str() ,fsm_ph->comment()->c_string()->c_str() ,DATE_AND_TIME() ,fsm_class->identifier()->identifier()->c_str() ,cc_str ); Op_str.write(big_buf_,x); Op_str<find(SDC_destructor); const char* dc_str(0); if(i != dir_map->end()){ @=T_destructor* dc = (T_destructor*)i->second;@>@/ dc_str = dc->syntax_code()->syntax_code()->c_str(); }else{ dc_str = ""; } x = sprintf(big_buf_ ,dtor_rtn_start ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,dc_str ); Op_str.write(big_buf_,x); Op_str<rules_alphabet()->size(); KCHARP delete_recycled_rules = @/ " for(int x = 0;x < %i;++x){\n"@/ " ///delete fsm_rules_reuse_table.per_rule_s_table_[x];\n"@/ " }"; x = sprintf(big_buf_ ,delete_recycled_rules ,no_rules ); Op_str.write(big_buf_,x); Op_str<find(SDC_failed); const char* fc_str(0); if(i != dir_map->end()){ @=T_failed* fc = (T_failed*)i->second;@>@/ fc_str = fc->syntax_code()->syntax_code()->c_str(); }else{ fc_str = " return false;"; } x = sprintf(big_buf_ ,failed_rtn ,fsm_class->identifier()->identifier()->c_str() ,fc_str ); Op_str.write(big_buf_,x); Op_str<find(SDC_op); const char* oc_str(0); if(i != dir_map->end()){ @=T_op* oc = (T_op*)i->second;@>@/ oc_str = oc->syntax_code()->syntax_code()->c_str(); }else{ oc_str = ""; } x = sprintf(big_buf_ ,op_rtn ,fsm_class->identifier()->identifier()->c_str() ,oc_str ); Op_str.write(big_buf_,x); Op_str<directives_map(); i = dir_map->find(SDC_user_implementation); if(i != dir_map->end()){ @=T_user_implementation* ui = (T_user_implementation*)i->second;@>@/ Op_str << ui->syntax_code()->syntax_code()->c_str()<+= void rules_subrules_implementations (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_rules_phrase* rules_ph = O2_RULES_PHASE; char big_buf_[BIG_BUFFER_32K]; @; } @*4 Go thru rules. @= RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); for(;i!=ie;++i){ rule_def* rd = *i; @; T_rule_lhs_phrase* rlhs = rd->rule_lhs(); const char* ctor_code(0); const char* dtor_code(0); string dtor_name("0"); const char* op_code(0); const char* user_imp_code(0); T_destructor* dtor(0); if(rlhs != 0){ SDC_MAP_type* dir_map = rlhs->lhs_directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_constructor); if(i != dir_map->end()){ @=T_constructor* ctor = (T_constructor*)i->second;@>@/ ctor_code = ctor->syntax_code()->syntax_code()->c_str(); } i = dir_map->find(SDC_destructor); if(i != dir_map->end()){ @=dtor = (T_destructor*)i->second;@>@/ dtor_code = dtor->syntax_code()->syntax_code()->c_str(); KCHARP dtor_nm = @/ "&dtor_%s"; sprintf(big_buf_ ,dtor_nm ,rd->rule_name()->c_str() ); dtor_name.clear(); dtor_name += big_buf_; } i = dir_map->find(SDC_op); if(i != dir_map->end()){ @=T_op* op = (T_op*)i->second;@>@/ op_code = op->syntax_code()->syntax_code()->c_str(); } i = dir_map->find(SDC_user_implementation); if(i != dir_map->end()){ @=T_user_implementation* uimp= (T_user_implementation*)i->second;@>@/ user_imp_code = uimp->syntax_code()->syntax_code()->c_str(); } } KCHARP rule_def_imp = @/ "%s::%s(yacco2::Parser* P)\n"@/ " :CAbs_lr1_sym\n"@/ " (\"%s\",%s,%s::R_%s_,P,%s,%s){\n"@/ "}\n"; string ad; if(rd->autodelete() == true){ ad += "true"; }else{ ad += "false"; } string ab; if(rd->autoabort() == true){ ab += "true"; }else{ ab += "false"; } int x = sprintf(big_buf_ ,rule_def_imp ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,dtor_name.c_str() ,fsm_class->identifier()->identifier()->c_str() ,rd->rule_name()->c_str() ,ad.c_str() ,ab.c_str() ); Op_str.write(big_buf_,x); Op_str<syntax_code()->syntax_code()->find("ABORT_STATUS"); KCHARP rule_def_dtor = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " bool ABORT_STATUS = ((yacco2::Parser*)P)->top_stack_record()->aborted__;\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; KCHARP rule_def_dtor_noabort = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; int x; if(r != std::string::npos){ // using abort status x = sprintf(big_buf_ ,rule_def_dtor ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,dtor_code ); }else{ x = sprintf(big_buf_ ,rule_def_dtor_noabort ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,dtor_code ); } Op_str.write(big_buf_,x); Op_str<rule_name()->c_str() ,op_code ); Op_str.write(big_buf_,x); Op_str<rule_name()->c_str() ,ctor_code ); Op_str.write(big_buf_,x); Op_str<; } @*4 Deal with arbitrator code.\fbreak @= if(rd->parallel_mntr() != 0){ @=T_parallel_monitor_phrase* pp_phrase = rd->parallel_mntr();@>@/ @; } @ @= if(pp_phrase->mntr_directives_map()->empty() != true){ SDC_MAP_type* pp_map = pp_phrase->mntr_directives_map(); SDC_MAP_ITER_type p = pp_map->find(SDC_arbitrator_code); if(p != pp_map->end()){ @=T_arbitrator_code* ac = (T_arbitrator_code*)p->second;@>@/ string::size_type idx = ac->syntax_code()->syntax_code()->find(CODE_PRESENCE_IN_ARBITRATOR_CODE); if(idx != string::npos){ KCHARP rule_s_arbitrator_imp =@/ "yacco2::THR _YACCO2_CALL_TYPE\n"@/ "%s::AR_%s(yacco2::Parser* Caller_pp){\n"@/ " yacco2::KCHARP ar_name = \"AR_%s\";\n"@/ "#include \"war_begin_code.h\"\n"@/ " %s\n"@/ "#include \"war_end_code.h\"\n"@/ "}\n"; int x = sprintf(big_buf_ ,rule_s_arbitrator_imp ,fsm_ph->namespace_id()->identifier()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,ac->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<px__| whereby the stacked item is x starting from 1. |p10__| would reference the tenth item on the stack. The parameter count starts from the left of the subrule. An epsilon subrule has no parameters. There is no c++ sdc validity check done here. It leaves it to the c++ compiler to digest \O2's code. @= T_subrules_phrase* sr_ph = rd->subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); int nosrs = sr_ph->no_subrules(); for(int xx = 1;j != je;++j,++xx){ T_subrule_def* srd = *j; AST* sr_t = AST::get_spec_child(*srd->subrule_s_tree(),1); int no_parms = srd->no_of_elems() - 1;// remove eos KCHARP sub_rule_def_imp = @/ "void %s::sr%i(){"; SDC_MAP_type* dir_map = srd->subrule_directives(); if(dir_map->empty() != true){ // gen if there is syntax directed code int x = sprintf(big_buf_ ,sub_rule_def_imp ,rd->rule_name()->c_str() ,xx ); Op_str.write(big_buf_,x); Op_str<find(SDC_op); if(i != dir_map->end()){ @=T_op* rhsc = (T_op*)i->second;@>@/ std::string::size_type idx; idx = rhsc->syntax_code()->syntax_code()->find("sf->p"); // any reference to the stack frame within the sdc? if(idx == std::string::npos) goto write_out_op_code; @; write_out_op_code:; KCHARP sub_rule_def_op_code = @/ " %s";// rhs op int x =sprintf(big_buf_ ,sub_rule_def_op_code ,rhsc->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<= for(int y=1;y<=no_parms;++y){// gen stack frame items bool eos_thd_or_proc(false); CAbs_lr1_sym* sym = AST::content(*sr_t); const char* parm_name(0); switch (sym->enumerated_id__){ case T_Enum::T_refered_rule_:{ @=refered_rule* rr = (refered_rule*)sym;@>@/ parm_name = rr->its_rule_def()->rule_name()->c_str(); break; } case T_Enum::T_refered_T_:{ @=refered_T* rt = (refered_T*)sym;@>@/ T_terminal_def* td = rt->its_t_def(); if(td->enum_id() == LR1_ALL_SHIFT_OPERATOR){ parm_name = "CAbs_lr1_sym"; break; } if(td->enum_id() == LR1_QUESTIONABLE_SHIFT_OPERATOR){ parm_name = "CAbs_lr1_sym"; break; } if(td->enum_id() == LR1_INVISIBLE_SHIFT_OPERATOR){ parm_name = "CAbs_lr1_sym"; break; } parm_name = td->classsym()->c_str(); break; } case T_Enum::T_T_identifier_:{ parm_name = "T_identifier"; break; } case T_Enum::T_T_NULL_:{ parm_name = "T_NULL"; break; } case T_Enum::T_T_2colon_:{ parm_name = "T_2colon"; break; } case T_Enum::T_T_called_thread_eosubrule_:{ eos_thd_or_proc = true; --no_parms; --y; break; } case T_Enum::T_T_null_call_thread_eosubrule_:{ eos_thd_or_proc = true; --no_parms; --y; break; } } if(y == 1){// stk frame KCHARP sub_rule_stk_parms_begin = @/ " struct SF{"; Op_str << sub_rule_stk_parms_begin< 0){ KCHARP sub_rule_stk_parms_end = @/ " };"; Op_str << sub_rule_stk_parms_end<parse_stack__.sf_by_top(%i);"; int x = sprintf(big_buf_ ,stk_frame_equate ,no_parms ); Op_str.write(big_buf_,x); Op_str<+= void OP_GRAMMAR_CPP(TOKEN_GAGGLE& Error_queue){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; string fn(fsm_ph->filename_id()->identifier()->c_str()); fn += Suffix_fsmimp; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_fsmcpp_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*fsm_ph->filename_id()); sym->set_who_created("o2externs.w - OP_GRAMMAR_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); fsm_cpp_includes(Op_file); using_ns_for_fsm_cpp(Op_file); rules_reuse_table_implementation(Op_file); fsm_class_implementation(Op_file); rules_subrules_implementations(Op_file); Op_file.close(); } @* Template of fsm sym file implementation: |OP_GRAMMAR_SYM|.\fbreak |reduce_rhs_of_rule| implementation and possible thread procedure. Back in time when c++ compilers were aborting i needed to split the size of the emitted grammar's source file so that digestion instead of indigestion took place. |reduce_rhs_of_rule| should be part of the ``fsm'' cpp file. I leave it as is cuz i'm lazy and makes reading of the outfiles easier. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) Include of grammar's header} \INDENT{.1in}{3) possible ``user-imp-sym'' code} \INDENT{.1in}{4) using namespaces} \INDENT{.1in}{5) Possible thread procedure implementation} \INDENT{.1in}{6) Fsm class |reduce_rhs_of_rule| implementation} \INDENT{.1in}{7) grammar's rules / subrules implementations} @*3 |user_imp_sym|.\fbreak There are times when circularity hits u in include definitions by the compiler's preprocessor. This allows the grammar writer to roll-his-own by injection points.\fbreak Grammar Directive: ``user-imp-sym'' for the emitted ``xxxsym.cpp'' code module. @+= void user_imp_sym (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); SDC_MAP_type* dir_map = fsm_class->directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_user_imp_sym); if(i != dir_map->end()){ @=T_user_imp_sym* ui = (T_user_imp_sym*)i->second;@>@/ Op_str << ui->syntax_code()->syntax_code()->c_str(); Op_str << endl; } } @*3 |thread_implementation|.\fbreak Some naming conventions:\fbreak ssPARSE\_TABLE is referenced out of the "wpp\_core.h" file. This was my way to import info into a canned thread code body.\fbreak @+= void thread_implementation (std::ofstream& Op_str){@/ T_parallel_parser_phrase* pp_ph = O2_PP_PHASE; if(pp_ph == 0){ Op_str <<"// monolithic grammar --- no thread" << endl; return; } T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_rules_phrase* rules_ph = O2_RULES_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP thread_definition = @/ "yacco2::THR _YACCO2_CALL_TYPE\n"@/ "%s::%s(yacco2::Parser* Caller_pp){\n"@/ " yacco2::Thread_entry& pp_thread_entry = I%s;\n"@/ " %s::%s %s_;// parallel-parser's parse table\n" "#define ssPARSE_TABLE %s_\n"@/ "#include \"wpp_core.h\"\n"@/ "}"; int x = sprintf(big_buf_ ,thread_definition ,fsm_ph->namespace_id()->identifier()->c_str() ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<namespace_id()->identifier()->c_str() ,pp_ph->pp_funct()->identifier()->identifier()->c_str() ,pp_ph->pp_funct()->identifier()->identifier()->c_str() // called proc name ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str()// static fsm ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() // static parser ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str()// new fsm ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str()// new parser ,fsm_ph->namespace_id()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() // set as proc\_parser ); Op_str.write(big_buf_,x); Op_str<+= void fsm_reduce_rhs_of_rule_implementation (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_rules_phrase* rules_ph = O2_RULES_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP reduce_rhs_of_rule_hdr = @/ "void \n"@/ "%s::reduce_rhs_of_rule\n"@/ " (yacco2::UINT Sub_rule_no,yacco2::Rule_s_reuse_entry** Recycled_rule){\n"@/ " int reducing_rule = rhs_to_rules_mapping_[Sub_rule_no];\n"@/ " Per_rule_s_reuse_table* rule_reuse_tbl_ptr = \n"@/ " fsm_rules_reuse_table.per_rule_s_table_[reducing_rule];\n"@/ " Rule_s_reuse_entry* re(0);\n"@/ " find_a_recycled_rule(rule_reuse_tbl_ptr,&re);\n" " (*Recycled_rule) = re;\n" " fnd_re: switch (Sub_rule_no){"; int x = sprintf(big_buf_ ,reduce_rhs_of_rule_hdr ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<; } @*3 Gen rules's subrules case statements. @= RULE_DEFS_TBL_ITER_type i = rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie = rules_ph->crt_order()->end(); for(;i!=ie;++i){ rule_def* rd = *i; @; } KCHARP default_case_and_end_sw = @/ " default: return;\n"@/ " }"; Op_str << default_case_and_end_sw<= const char* rule_s_ctor = "// no rule's constructor directive"; T_rule_lhs_phrase* rlhs = rd->rule_lhs(); if (rlhs != 0) { SDC_MAP_type* rdlhs_map = rlhs->lhs_directives_map(); if(rdlhs_map != 0){ SDC_MAP_ITER_type kkk = rdlhs_map->find(SDC_constructor); if(kkk != rdlhs_map->end()){ @=T_constructor* cc = (T_constructor*)kkk->second;@>@/ rule_s_ctor = "sym->ctor();\n"; } } } T_subrules_phrase* sr_ph = rd->subrules(); SUBRULE_DEFS_ITER_type j = sr_ph->subrules()->begin(); SUBRULE_DEFS_ITER_type je = sr_ph->subrules()->end(); KCHARP case_stmt_parta = @/ " case rhs%i_%s_:{\n"@/ " %s* sym;\n"@/ " if(re->rule_ == 0){\n"@/ " sym = new %s(parser__);\n"@/ " re->rule_ = sym;\n"@/ " }else{\n"@/ " sym = (%s*)re->rule_;\n"@/ " }\n"@/ " %s\n"@/ " (*Recycled_rule)->rule_ = sym;\n"@/ " sym->rule_info__.rhs_no_of_parms__ = %i;\n"@/ " \n"; KCHARP case_stmt_partb = " sym->sr%i();"; KCHARP case_stmt_partc = " return;}"; for(;j != je;++j){ T_subrule_def* srd = *j; int no_parms = srd->no_of_elems() - 1;// remove eos // see if subrule is a thread or proc call so also remove its eos AST* sr_t = srd->subrule_s_tree(); @=set eos_filter;@>@/ eos_filter.insert(T_Enum::T_T_called_thread_eosubrule_); eos_filter.insert(T_Enum::T_T_null_call_thread_eosubrule_); tok_can_ast_functor walk_the_plank_mate; ast_prefix_1forest eos_walk (*sr_t,&walk_the_plank_mate,&eos_filter,ACCEPT_FILTER);@/ tok_can eos_can(eos_walk); for (int xxx = 0;eos_can.operator[](xxx) != yacco2::PTR_LR1_eog__;++xxx); if(eos_can.empty() ==false) --no_parms; int x = sprintf(big_buf_ ,case_stmt_parta ,srd->subrule_no_of_rule() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rd->rule_name()->c_str() ,rule_s_ctor ,no_parms); Op_str.write(big_buf_,x); Op_str<subrule_directives(); if(!sdrmap->empty()){ // any syntax directed code, call it int x = sprintf(big_buf_,case_stmt_partb,srd->subrule_no_of_rule()); Op_str.write(big_buf_,x); Op_str<rule_lhs() != 0){ SDC_MAP_type* xxrdmap = rd->rule_lhs()->lhs_directives_map(); if(xxrdmap != 0){ SDC_MAP_ITER_type kkk = xxrdmap->find(SDC_op); if(kkk != xxrdmap->end()){ Op_str << " sym->op();"<+= void OP_GRAMMAR_SYM(TOKEN_GAGGLE& Error_queue){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; string fn(fsm_ph->filename_id()->identifier()->c_str()); fn += Suffix_fsmsym; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_fsmsym_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*fsm_ph->filename_id()); sym->set_who_created("o2externs.w - OP_GRAMMAR_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); user_imp_sym(Op_file); fsm_cpp_includes(Op_file); using_ns_for_fsm_cpp(Op_file); thread_implementation(Op_file); fsm_reduce_rhs_of_rule_implementation(Op_file); Op_file.close(); } @* Template of fsm tbl file implementation: |OP_GRAMMAR_TBL|.\fbreak Emit the grammar's lr1 states tables, and threads for dispatch tables along with their arbitrators. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) Include of grammar's header} \INDENT{.1in}{3) possible ``user-imp-tbl'' code} \INDENT{.1in}{4) using namespaces} \INDENT{.1in}{5) lookahead sets} \INDENT{.1in}{6) lr states externs and thread table definitions / implementations} \INDENT{.1in}{7) lr states definitions / implementations} \fbreak Please see \O2{}'s documentation describing the following tables and their layouts:\fbreak \INDENT{.2in}{1) State} \INDENT{.2in}{2) Shift\_entry} \INDENT{.2in}{3) Reduce\_tbl} \INDENT{.2in}{4) State\_s\_thread\_tbl} \INDENT{.2in}{5) Thread\_entry} @*3 |user_imp_tbl|.\fbreak There are times when circularity hits u in include definitions by the compiler's preprocessor. This allows the grammar writer to roll-his-own by injection points.\fbreak Grammar Directive: ``user-imp-tbl'' for the emitted ``xxxtbl.cpp'' code module. @+= void user_imp_tbl (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); SDC_MAP_type* dir_map = fsm_class->directives_map(); SDC_MAP_ITER_type i = dir_map->find(SDC_user_imp_tbl); if(i != dir_map->end()){ @=T_user_imp_tbl* ui = (T_user_imp_tbl*)i->second;@>@/ Op_str << ui->syntax_code()->syntax_code()->c_str(); Op_str << endl; } } @*3 |output_la_sets|.\fbreak @+= void output_la_sets (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; COMMON_LA_SETS_ITER_type i = COMMON_LA_SETS.begin(); COMMON_LA_SETS_ITER_type ie = COMMON_LA_SETS.end(); for(int idx=0;i!=ie;++i,++idx){ LA_SET_type* la_set = *i; @; @; } } @*3 List literal entries of la set.\fbreak Leave T traces of what makes up the compressed bit table. @= KCHARP la_set_entry_literal =@/ "// %s"; LA_SET_ITER_type j = la_set->begin(); LA_SET_ITER_type je = la_set->end(); for(;j!=je;++j){ T_in_stbl* tsym = *j; int x = sprintf(big_buf_ ,la_set_entry_literal ,tsym->t_def()->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<= KCHARP la_set_hdr =@/ "yacco2::UCHAR LA%i_%s[] ={"; int x = sprintf(big_buf_ ,la_set_hdr ,idx + 1 // name given to la set uses the set no relative to 1 ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<; @; @*4 Build la set's bit table.\fbreak @= BIT_MAP_type paired_set; LA_SET_ITER_type k = la_set->begin(); LA_SET_ITER_type ke = la_set->end(); int en_no(-1); for(;k != ke;++k){// walk the items in set T_in_stbl* T = *k; en_no = T->t_def()->enum_id(); // calculate partition no and its set of elements int Q = en_no / NO_BITS_PER_SET_PARTITION; int R = en_no % NO_BITS_PER_SET_PARTITION; int One(1); int E_v = One << R; BIT_MAP_ITER_type e = paired_set.find(Q); if(e == paired_set.end()){ paired_set[Q] = E_v; }else{ int se = e->second; int v = se + E_v; e->second = v; } } @*4 Output the la set's compressed bits.\fbreak @= KCHARP no_la_set_entries =@/ "%i"; x = sprintf(big_buf_ ,no_la_set_entries ,paired_set.size() ); Op_str.write(big_buf_,x); Op_str<first; int e_v = kl->second; KCHARP la_set_entry =@/ ",%i,%i"; x = sprintf(big_buf_ ,la_set_entry ,part_no ,e_v ); Op_str.write(big_buf_,x); Op_str<+= void externs_and_thread_tbl_defs (std::ofstream& Op_str){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; STATES_ITER_type i = LR1_STATES.begin(); STATES_ITER_type ie = LR1_STATES.end(); int state_cnt(0); for(;i!=ie;++i){ state* s = *i; ++state_cnt; KCHARP state_extern = @/ "extern yacco2::State S%i_%s;"; int x = sprintf(big_buf_ ,state_extern ,state_cnt ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<; } } @*4 Deal with threads in state.\fbreak Emit their thread table definitions along with arbitrator code. @= using namespace NS_yacco2_T_enum; if(s->vectored_into_by_elem_sym_ == 0) continue;// next state switch(s->vectored_into_by_elem_){ case LR1_PARALLEL_OPERATOR:{ @; @; break; } default: continue;// next state } @*5 Determine number of threads to call.\fbreak Read the core items of the state. At the same time find out the rule that could have arbitration. Watch out for $\vert\vert\vert$ being a returned T from a called thread. If so this is not your average bear call. @= string ar_name; rule_def* ar_s_rule_s_name(0); int no_of_threads(0); S_VECTORS_ITER_type j = s->state_s_vector_.begin();// rtn T's from threads S_VECTORS_ITER_type je = s->state_s_vector_.end(); for(;j!=je;++j){// read subrules of returned Ts from called threads S_VECTOR_ELEMS_type& elem_list = j->second; S_VECTOR_ELEMS_ITER_type k = elem_list.begin(); S_VECTOR_ELEMS_ITER_type ke = elem_list.end(); for(;k!=ke;++k){// walk along the call sequence 1st item is T state_element* se = *k; if(se->next_state_element_ == 0)continue;// not your thread call but rtned T CAbs_lr1_sym*sym= AST::content(*AST::brother(*se->sr_element_));// not part of state: bypassed if(sym->enumerated_id__!=T_Enum::T_T_called_thread_eosubrule_)continue; rule_def* rd = se->subrule_def_->its_rule_def(); RULES_HAVING_AR_ITER_type ai = RULES_HAVING_AR.find(rd); if(ai != RULES_HAVING_AR.end()){ ar_s_rule_s_name = rd; } ++no_of_threads; } } if(ar_s_rule_s_name != 0){ ar_name += "AR_"; ar_name += ar_s_rule_s_name->rule_name()->c_str(); s->arbitrator_name_ = new string(ar_name); // add arbitration code to the state } @*5 Output called threads table def / imp.\fbreak To get the called threads for the dispatch table, one must look 1 state ahead cuz this state is the returned T from a called thread expression. Remember there are 2 types of thread call expressions:\fbreak \INDENT{.3in}{1) $\vert\vert\vert$ T NS\_xxx::TH\_eol --- real called thread} \INDENT{.3in}{2) $\vert\vert\vert$ T NULL --- expr to field a returned T from 1)} @= if(no_of_threads > 0){ KCHARP state_s_thread_tbl_def = @/ "struct S%ittd_%s{\n"@/ " yacco2::USINT no_entries_;\n"@/ " yacco2::Type_pp_fnct_ptr ar_fnct_ptr_;\n"@/ " yacco2::ULINT (*thd_id_bit_map_ptr__)[];\n"@/ " yacco2::Thread_entry* thread_entries_[%i];\n"@/ "};"; int x = sprintf(big_buf_ ,state_s_thread_tbl_def ,state_cnt ,fsm_class->identifier()->identifier()->c_str() ,no_of_threads ); Op_str.write(big_buf_,x); Op_str<; KCHARP state_s_thread_tbl_imp = @/ "S%ittd_%s S%itt_%s = {\n"@/ " %i // no of threads\n"@/ " ,%s //AR_rulename or 0\n"@/ " ,0// ptr to thread id bit map"; x = sprintf(big_buf_ ,state_s_thread_tbl_imp ,state_cnt ,fsm_class->identifier()->identifier()->c_str() ,state_cnt ,fsm_class->identifier()->identifier()->c_str() ,no_of_threads ,ar_name.c_str() ); Op_str.write(big_buf_,x); Op_str<state_s_vector_.begin();// read core items of state je = s->state_s_vector_.end(); for(;j!=je;++j){// read subrules of returned Ts from called threads S_VECTOR_ELEMS_type& elem_list = j->second; S_VECTOR_ELEMS_ITER_type k = elem_list.begin(); S_VECTOR_ELEMS_ITER_type ke = elem_list.end(); for(;k!=ke;++k){// walk along call sequence 1st item is T state_element* se = *k; if(se->next_state_element_ == 0)continue;// not thd call but CAbs_lr1_sym*sym= AST::content(*AST::brother(*se->sr_element_));// not part of state: bypassed if(sym->enumerated_id__==T_Enum::T_T_called_thread_eosubrule_){ @=T_called_thread_eosubrule* called_th = (T_called_thread_eosubrule*)sym;@>@/ KCHARP state_s_thread_tbl_entry = @/ " ,(yacco2::Thread_entry*)&I%s"; int x = sprintf(big_buf_ ,state_s_thread_tbl_entry ,called_th->called_thread_name()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<= S_VECTORS_ITER_type j = se->goto_state_->state_s_vector_.begin();// rtn T's from threads S_VECTORS_ITER_type je = se->goto_state_->state_s_vector_.end(); for(;j!=je;++j){// read subrules of returned Ts from called procedure S_VECTOR_ELEMS_type& elem_list = j->second; S_VECTOR_ELEMS_ITER_type k = elem_list.begin(); S_VECTOR_ELEMS_ITER_type ke = elem_list.end(); for(;k!=ke;++k){// walk along call sequence 1st item is T state_element* se = *k; if(se->next_state_element_ == 0)continue;// not thd call but AST* bypassed_thd_eos_t = AST::brother(*se->sr_element_); CAbs_lr1_sym* sym = AST::content(*bypassed_thd_eos_t); if(sym->enumerated_id__ == T_Enum::T_T_called_thread_eosubrule_){ @=T_called_thread_eosubrule* called_th = (T_called_thread_eosubrule*)sym;@>@/ KCHARP state_s_thread_tbl_entry = @/ ",(yacco2::Type_pc_fnct_ptr)&%s::%s"; int x = sprintf(big_buf_ ,state_s_thread_tbl_entry ,called_th->ns()->identifier()->c_str() ,called_th->called_thread_name()->identifier()->c_str() ); Op_str.write(big_buf_,x); } } } @*5 determine if there is a rule's arbitrator to call.\fbreak This conditions on whether arbritrator code is present in some spawning rules. Backtrack to the calling state to figure out whether there is a rule that contains code. If not then set the rule name to``0'' meaning nada to arbitrate. If found, the arbitrator's procedure's name is AR\_rulename. @= if(ar_name.empty() == true) ar_name += "0"; @*5 |determine_shift_element_name|.\fbreak For your eyes only... @+= const char* determine_shift_element_name(CAbs_lr1_sym* Sym){ switch(Sym->enumerated_id__){ case T_Enum::T_T_terminal_def_:{ @=T_terminal_def* td = (T_terminal_def*)Sym;@>@/ return td->classsym()->c_str(); } case T_Enum::T_rule_def_:{ @=rule_def* rd = (rule_def*)Sym;@>@/ return rd->rule_name()->c_str(); } } return "element not found"; } @*4 Output 1st state's shift table.\fbreak Hardwire ``start rule'' accept shift. The entries are in ascending order of their enumerates. Make sure the hardwired ``start rule'' shift entry comes right after the T vocabulary cuz of balanced table requirement. One wrinkle is if the 1st state does not contain any rules --- only terminals. So check at the end of the generation for this situation to gen the shifted start rule. @+= void output_1st_state_s_shift_table(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); T_rules_phrase* rules_ph = O2_RULES_PHASE; rule_def* s_rule = (*rules_ph->crt_order())[0]; int s_rule_enum_no = s_rule->enum_id(); char big_buf_[BIG_BUFFER_32K]; S_VECTORS_ITER_type svi; S_VECTORS_ITER_type svie; @; ++no_of_shift_items;// add start rule shift bool first_or_2nd_prt(false); bool accept_prted(false); @; svi = State.state_s_vector_.begin(); svie = State.state_s_vector_.end(); for(;svi!=svie;++svi){// walk the vectors S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int cur_se_enum_no = svi->first; @; int goto_state_no = se->goto_state_->state_no_; const char* shift_elem_literal = determine_shift_element_name(se->sr_def_element_); if(cur_se_enum_no < s_rule_enum_no){ @; }else{ if(accept_prted == false){ accept_prted = true; @; } @;// balance of rules shifted } } if(accept_prted == false){ accept_prted = true; @; } @; } @*5 Print 1st or 2nd shift entry.\fbreak @= if (first_or_2nd_prt == false) { first_or_2nd_prt = true; KCHARP imp_of_state_s_shift_1st_entry =@/ " {%i,(State*)&S%i_%s} // shift sym: %s"; int x = sprintf(big_buf_ ,imp_of_state_s_shift_1st_entry ,cur_se_enum_no ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ,shift_elem_literal ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ,shift_elem_literal ); Op_str.write(big_buf_,x); Op_str<= if (first_or_2nd_prt == false) { first_or_2nd_prt = true; KCHARP imp_of_state_s_shift_1st_entry =@/ " {%i,(State*)&S%i_%s} // accept sym: %s"; int x = sprintf(big_buf_ ,imp_of_state_s_shift_1st_entry ,s_rule_enum_no ,1 ,fsm_class->identifier()->identifier()->c_str() ,s_rule->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ,s_rule->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<= KCHARP def_of_state_s_shift_entries =@/ "struct S%istd_%s{\n"@/ " yacco2::USINT no_entries_;\n"@/ " yacco2::Shift_entry shift_entries_[%i];\n"@/ "};"; int x = sprintf(big_buf_ ,def_of_state_s_shift_entries ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,no_of_shift_items ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,no_of_shift_items ); Op_str.write(big_buf_,x); Op_str<= KCHARP imp_of_shift_entries_end =@/ " }// end of shift table\n"@/ "};"; Op_str << imp_of_shift_entries_end<+= void output_all_others_state_s_shift_table(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; bool first_or_2nd_prt(false); S_VECTORS_ITER_type svi; S_VECTORS_ITER_type svie; @; if(no_of_shift_items){ @; S_VECTORS_ITER_type svi = State.state_s_vector_.begin(); S_VECTORS_ITER_type svie = State.state_s_vector_.end(); for(;svi!=svie;++svi){// walk the vectors S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int cur_se_enum_no= svi->first; @; int goto_state_no = se->goto_state_->state_no_; const char* shift_elem_literal = determine_shift_element_name(se->sr_def_element_); @; } @; } } @*5 Determine number of shifts.\fbreak @= int no_of_shift_items(0); svi = State.state_s_vector_.begin(); svie = State.state_s_vector_.end(); for(;svi!=svie;++svi){// walk the vectors S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int cur_se_enum_no = svi->first; @; ++no_of_shift_items; } @*5 Bypass meta T.\fbreak The meta T are bypassed as each are implemented separately as individual entries within the state table. The real truth is the individual entries in the state are for a fast determination of their presence to sequentially attempt the fsm's order of operations: parallelism --- thread calls by $\vert\vert\vert$ expressions, specific shift on current terminal token, invisible shift -- $\vert.\vert$ implicit espilon, all-shift $\vert+\vert$, and reduce. Their presence in the shift table is REQUIRED for the shift operator. On 1 hand lookup speed by separate state's entries is fast but when it comes to shifting it's slow to sequentially check for their individual presence. So below is my optimized mistake. THIS CODE IS NOW VOID! Only here for removal justifications. Meta T returned from a called thread are exempt and are part of the shift table. Why? They are treated like any other T returned from a called thread. The returned T is shifted into its reducing state that reduces the called-thread expression. @= switch(cur_se_enum_no){ case LR1_PARALLEL_OPERATOR:{ if(se->previous_state_element_ == 0) continue;//bypass start of call thd expr if(// is $\vert\vert\vert$ returned from thread se->previous_state_element_->its_enum_id_ == LR1_PARALLEL_OPERATOR) break;// do not bypass: part of shift tbl continue;// bypass } case LR1_FSET_TRANSIENCE_OPERATOR:{ if(se->previous_state_element_ == 0) continue;//bypass start of call proc expr if(// is \\TRAshift returned from thread se->previous_state_element_->its_enum_id_ == LR1_PARALLEL_OPERATOR) break;// do not bypass: part of shift tbl continue;// bypass } case LR1_INVISIBLE_SHIFT_OPERATOR:{ if(se->previous_state_element_ == 0) continue;// bypass dealt a separate state entry if(// is $\vert.\vert$ returned from thread se->previous_state_element_->its_enum_id_ == LR1_PARALLEL_OPERATOR) break;// do not bypass: part of shift tbl continue;// bypass } case LR1_ALL_SHIFT_OPERATOR:{ if(se->previous_state_element_ == 0) continue; if(// is $\vert+\vert$ returned from thread se->previous_state_element_->its_enum_id_ == LR1_PARALLEL_OPERATOR) break;// do not bypass: part of shift tbl continue;// bypass } case LR1_QUESTIONABLE_SHIFT_OPERATOR:{ if(se->previous_state_element_ == 0) continue; if(// is $\vert?\vert$ returned from thread se->previous_state_element_->its_enum_id_ == LR1_PARALLEL_OPERATOR) break;// do not bypass: part of shift tbl continue;// bypass } } @*5 Bypass reduces. @= if(cur_se_enum_no >=0){ // not a reduce T }else{ switch(cur_se_enum_no){ case -T_Enum::T_T_eosubrule_: { if(se->next_state_element_ == 0)continue;// real reduce: epsilon so bypass if(se->previous_state_element_ == 0)continue;// real reduce: epsilon so bypass break; } default:{ break;//not a reduce } } } @*4 Output meta shifts separately.\fbreak @+= void output_meta_shifts_separately(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; S_VECTORS_ITER_type svi = State.state_s_vector_.find(LR1_PARALLEL_OPERATOR); S_VECTORS_ITER_type svie = State.state_s_vector_.end(); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP parallel_entry = @/ "yacco2::Shift_entry S%ipse_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,parallel_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_PARALLEL_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP invisible_shift_entry = @/ "yacco2::Shift_entry S%iise_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,invisible_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_INVISIBLE_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP all_shift_entry = @/ "yacco2::Shift_entry S%iase_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,all_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_ALL_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP procedure_call_entry = @/ "yacco2::Shift_entry S%ipcse_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,procedure_call_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_FSET_TRANSIENCE_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP questionable_shift_entry = @/ "yacco2::Shift_entry S%iqse_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,questionable_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_QUESTIONABLE_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void output_state_s_reduced_table(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; @; @; } @*5 Determine number of reduces.\fbreak Each shift / reduce entry in the state contains a list of subrules partaking in the activity. For the reduce activity this is why the number of entries in the list is accumulated. More than 1 entry indicates a reduce / reduce type conflict. As reduced vectors are $<$ 0 and the state's vector is a map ordered on the enumerate value, sequentially reading the map is not an inefficient way to deal with these items as they are the first to be read. The only wrinkle is when a ``eosubrule'' is returned from a called thread. This is not an end-of-subrule. @= int no_of_reduces(0); S_VECTORS_ITER_type j = State.state_s_vector_.begin(); S_VECTORS_ITER_type je = State.state_s_vector_.end(); for(;j!=je;++j){// read list of subrules per vector int cur_se_enum_no = j->first; S_VECTOR_ELEMS_ITER_type seli = j->second.begin(); state_element* se = *seli; if(cur_se_enum_no >=0)break;// not a reduce T switch (cur_se_enum_no){ case -T_Enum::T_T_eosubrule_:{ if(se->next_state_element_ == 0){// real eos no_of_reduces += j->second.size(); continue; } } } } @*5 Output reduced table def / imp.\fbreak @= if(no_of_reduces > 0){ KCHARP reduce_def = @/ "struct S%irtd_%s{\n"@/ " yacco2::USINT no_entries_;\n"@/ " yacco2::Reduce_entry reduce_entries_[%i];\n"@/ "};"; int x = sprintf(big_buf_ ,reduce_def ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,no_of_reduces ); Op_str.write(big_buf_,x); Op_str<identifier()->identifier()->c_str() ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,no_of_reduces ); Op_str.write(big_buf_,x); Op_str<; KCHARP reduce_imp_end = @/ " }// end of reduce table\n"@/ "};"; Op_str << reduce_imp_end<= j = State.state_s_vector_.begin();// read core items of state je = State.state_s_vector_.end(); int reduce_no(-1); for(;j!=je;++j){ int cur_se_enum_no = j->first; if(cur_se_enum_no >=0)break; S_VECTOR_ELEMS_type& elem_list = j->second; S_VECTOR_ELEMS_ITER_type k = elem_list.begin(); S_VECTOR_ELEMS_ITER_type ke = elem_list.end(); for(;k!=ke;++k){// la sets ++reduce_no; state_element* se = *k; if(reduce_no == 0){ KCHARP reduce_imp_1st_entry = @/ " {(Set_tbl*)&LA%i_%s,%s::rhs%i_%s_}"; int x = sprintf(big_buf_ ,reduce_imp_1st_entry ,se->common_la_set_idx_+1 ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,se->subrule_def_->subrule_no_of_rule() ,se->subrule_def_->its_rule_def()->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<common_la_set_idx_+1 ,fsm_class->identifier()->identifier()->c_str() ,fsm_class->identifier()->identifier()->c_str() ,se->subrule_def_->subrule_no_of_rule() ,se->subrule_def_->its_rule_def()->rule_name()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void output_state_s_called_threads_table(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; KCHARP call_thread_table_imp = @/ "yacco2::Shift_entry S%ipse_%s = \n"@/ "{%i,(State*)&S%i_%s};\n"; } @*4 Output state's called procedure table.\fbreak @+= void output_state_s_called_procedure_table(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; KCHARP call_thread_table_imp = @/ "yacco2::Shift_entry S%ipcse_%s = \n"@/ "{%i,(State*)&S%i_%s};\n"; } @*4 Output meta shifts separately.\fbreak @+= void output_questionable_shift(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; S_VECTORS_ITER_type svi = State.state_s_vector_.find(LR1_PARALLEL_OPERATOR); S_VECTORS_ITER_type svie = State.state_s_vector_.end(); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP parallel_entry = @/ "yacco2::Shift_entry S%ipse_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,parallel_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_PARALLEL_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP invisible_shift_entry = @/ "yacco2::Shift_entry S%iise_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,invisible_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_INVISIBLE_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP all_shift_entry = @/ "yacco2::Shift_entry S%iase_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,all_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_ALL_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP procedure_call_entry = @/ "yacco2::Shift_entry S%ipcse_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,procedure_call_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_FSET_TRANSIENCE_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP questionable_shift_entry = @/ "yacco2::Shift_entry S%iise_%s = {%i,(State*)&S%i_%s};"; int x = sprintf(big_buf_ ,questionable_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,LR1_QUESTIONABLE_SHIFT_OPERATOR ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void output_lr_state(std::ofstream& Op_str,state& State){ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; T_fsm_class_phrase* fsm_class = fsm_ph->fsm_class_phrase(); char big_buf_[BIG_BUFFER_32K]; KCHARP state_entry = @/ "yacco2::State S%i_%s = //State's vectored into symbol: \"%s\" \n" "{%i"; int x = sprintf(big_buf_ ,state_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ,State.entry_symbol_literal() ,State.state_no_ ); Op_str.write(big_buf_,x); Op_str<; @; @; @; @; @; @; @; @; Op_str << "};"<= S_VECTORS_ITER_type svi = State.state_s_vector_.find(LR1_PARALLEL_OPERATOR); S_VECTORS_ITER_type svie = State.state_s_vector_.end(); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP parallel_entry = @/ ",(Shift_entry*)&S%ipse_%s"; int x = sprintf(big_buf_ ,parallel_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); }else{ Op_str << ",0"; } @*5 All shift entry.\fbreak @= svi = State.state_s_vector_.find(LR1_ALL_SHIFT_OPERATOR); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP all_shift_entry = @/ ",(Shift_entry*)&S%iase_%s"; int x = sprintf(big_buf_ ,all_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<= svi = State.state_s_vector_.find(LR1_INVISIBLE_SHIFT_OPERATOR); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP invisible_shift_entry = @/ ",(Shift_entry*)&S%iise_%s"; int x = sprintf(big_buf_ ,invisible_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<= svi = State.state_s_vector_.find(LR1_FSET_TRANSIENCE_OPERATOR); svie = State.state_s_vector_.end(); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP procedure_call_entry = @/ ",(Shift_entry*)&S%ipcse_%s"; int x = sprintf(big_buf_ ,procedure_call_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); }else{ Op_str << ",0"; } @*5 Questionable shift entry.\fbreak @= svi = State.state_s_vector_.find(LR1_QUESTIONABLE_SHIFT_OPERATOR); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; int goto_state_no = se->goto_state_->state_no_; KCHARP questionable_shift_entry = @/ ",(Shift_entry*)&S%iqse_%s"; int x = sprintf(big_buf_ ,questionable_shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<= @; if(no_of_shift_items > 0){ KCHARP shift_entry = @/ ",(Shift_tbl*)&S%ist_%s"; int x = sprintf(big_buf_ ,shift_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); }else{ Op_str << ",0"; } @*5 Reduce entry.\fbreak @= @; if(no_of_reduces > 0){ KCHARP reduce_entry = @/ ",(Reduce_tbl*)&S%irt_%s"; int x = sprintf(big_buf_ ,reduce_entry ,State.state_no_ ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); }else{ Op_str << ",0"; } @*5 Thread call entry.\fbreak Watch out for \PARshift being a returned T from a called thread. If so this is not your average bear call. @= svi = State.state_s_vector_.find(LR1_PARALLEL_OPERATOR); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; if(se->next_state_element_->goto_state_ == 0){ Op_str << ",0"; }else{ int goto_state_no = se->goto_state_->state_no_; KCHARP thread_calls_entry = @/ ",(State_s_thread_tbl*)&S%itt_%s"; int x = sprintf(big_buf_ ,thread_calls_entry ,goto_state_no ,fsm_class->identifier()->identifier()->c_str() ); Op_str.write(big_buf_,x); } }else{ Op_str << ",0"; } @*5 Procedure call function entry.\fbreak Watch out for \TRAshift being a returned T from a called thread. If so this is not your average bear call. @= svi = State.state_s_vector_.find(LR1_FSET_TRANSIENCE_OPERATOR); if(svi != svie){ S_VECTOR_ELEMS_ITER_type seli = svi->second.begin(); state_element* se = *seli; if(se->next_state_element_->goto_state_ == 0){ Op_str << ",0";// rtned \\TRAshift from a \\PARshift }else{ @; } }else{ Op_str << ",0"; // no procedure call } @*3 |emit_each_lr_state_s_tables|.\fbreak Actions that can occur within a state:\fbreak \INDENT{.3in}{1) shift} \INDENT{.3in}{2) reduce} \INDENT{.3in}{3) accept} \INDENT{.3in}{4) called threads} \INDENT{.3in}{5) called procedure} @+= void emit_each_lr_state_s_tables(std::ofstream& Op_str){ STATES_ITER_type si = LR1_STATES.begin(); STATES_ITER_type sie = LR1_STATES.end(); for(;si!=sie;++si){// walk the states state* cur_state = *si; if(cur_state->state_no_ == 1){ output_1st_state_s_shift_table(Op_str,*cur_state); }else{ output_all_others_state_s_shift_table(Op_str,*cur_state); } output_meta_shifts_separately(Op_str,*cur_state); output_state_s_reduced_table(Op_str,*cur_state); output_state_s_called_threads_table(Op_str,*cur_state); output_state_s_called_procedure_table(Op_str,*cur_state); output_lr_state(Op_str,*cur_state); } } @*2 |OP_GRAMMAR_TBL| implementation.\fbreak @+= void OP_GRAMMAR_TBL(TOKEN_GAGGLE& Error_queue){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; string fn(fsm_ph->filename_id()->identifier()->c_str()); fn += Suffix_fsmtbl; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_fsmtbl_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*fsm_ph->filename_id()); sym->set_who_created("o2externs.w - OP_GRAMMAR_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); user_imp_tbl(Op_file); fsm_cpp_includes(Op_file); using_ns_for_fsm_cpp(Op_file); output_la_sets(Op_file); externs_and_thread_tbl_defs(Op_file); emit_each_lr_state_s_tables(Op_file); Op_file.close(); } @* Template of enumeration header: |OP_ENUMERATION_HEADER|.\fbreak Count those terminals. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) enumeration include guard declaration} \INDENT{.2in}{2.1) namespace declaration of enumeration scheme} \INDENT{.3in}{2.1.1) start definition of T\_enum structure} \INDENT{.3in}{2.1.1.1) begin declaration of c enum list} \INDENT{.3in}{2.1.1.1.1) summary of vocabulary classes} \INDENT{.3in}{2.1.1.1.2) enumerate lrk} \INDENT{.3in}{2.1.1.1.3) enumerate raw characters} \INDENT{.3in}{2.1.1.1.4) enumerate meta terminals} \INDENT{.3in}{2.1.1.1.5) enumerate errors} \INDENT{.3in}{2.1.2) close off enum list} \INDENT{.3in}{2.1.2) close off T\_enum structure} \INDENT{.3in}{2.2) close off namespace definition} \INDENT{.1in}{3) close off include guard declaration} @*3 Enumerate the LR constants classification: |enumerate_lrk|.\fbreak Note that the manufactured enumerate name is composed of a prefix ``T\_'', its class name, and suffixed with ``\_''. @+= void enumerate_lrk (std::ofstream& Op_str){@/ T_lr1_k_phrase* ph = O2_LRK_PHASE; std::vector* dictionary = ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP enumerate_item = @/ " ,T_%s_ = %i"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; int x = sprintf(big_buf_ ,enumerate_item ,td->classsym()->c_str() ,td->enum_id() ); Op_str.write(big_buf_,x); Op_str<+= void enumerate_rc (std::ofstream& Op_str){@/ T_rc_phrase* ph = O2_RC_PHASE; std::vector* dictionary = ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP enumerate_item = @/ " ,T_%s_ = %i"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; int x = sprintf(big_buf_ ,enumerate_item ,td->classsym()->c_str() ,td->enum_id() ); Op_str.write(big_buf_,x); Op_str<+= void enumerate_errors (std::ofstream& Op_str){@/ T_error_symbols_phrase* ph = O2_ERROR_PHASE; std::vector* dictionary = ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP enumerate_item = @/ " ,T_%s_ = %i"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; int x = sprintf(big_buf_ ,enumerate_item ,td->classsym()->c_str() ,td->enum_id() ); Op_str.write(big_buf_,x); Op_str<+= void enumerate_T (std::ofstream& Op_str){@/ T_terminals_phrase* ph = O2_T_PHASE; std::vector* dictionary = ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP enumerate_item = @/ " ,T_%s_ = %i"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; int x = sprintf(big_buf_ ,enumerate_item ,td->classsym()->c_str() ,td->enum_id() ); Op_str.write(big_buf_,x); Op_str<+= void enumeration_summary_for_struct (std::ofstream& Op_str){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP summary = @/ " sum_total_T = %i\n"@/ " ,no_of_terminals = %i\n"@/ " ,no_of_raw_chars = %i\n"@/ " ,no_of_lr1_constants = %i\n"@/ " ,no_of_error_terminals = %i\n"@/ " ,start_LRK=%i,end_LRK=%i\n"@/ " ,start_RC=%i,end_RC=%i\n"@/ " ,start_T=%i,end_T=%i\n"@/ " ,start_ERR=%i,end_ERR=%i\n"@/ " ,start_R=%i"; int x = sprintf(big_buf_ ,summary ,enum_ph->total_enumerate() ,enum_ph->total_T_enumerate() ,enum_ph->total_rc_enumerate() ,enum_ph->total_lrk_enumerate() ,enum_ph->total_err_enumerate() ,enum_ph->start_lrk_enumerate(),enum_ph->stop_lrk_enumerate() ,enum_ph->start_rc_enumerate(),enum_ph->stop_rc_enumerate() ,enum_ph->start_T_enumerate(),enum_ph->stop_T_enumerate() ,enum_ph->start_err_enumerate(),enum_ph->stop_err_enumerate() ,enum_ph->total_enumerate() ); Op_str.write(big_buf_,x); Op_str<+= void enumeration_define_list (std::ofstream& Op_str){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP enum_list_start = @/ " enum enumerated_terminals%s{"; int x = sprintf(big_buf_ ,enum_list_start," " ); Op_str.write(big_buf_,x); Op_str<+= void enumeration_define_structure (std::ofstream& Op_str){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP struct_start = @/ " struct T_Enum%s{"; int x = sprintf(big_buf_ ,struct_start," " ); Op_str.write(big_buf_,x); Op_str<+= void enumeration_namespace_for_header (std::ofstream& Op_str){@/ using namespace NS_yacco2_terminals; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP ns_of_enum_start = @/ "namespace %s {"; int x = sprintf(big_buf_ ,ns_of_enum_start ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void enumeration_include_guard_for_header (std::ofstream& Op_str){@/ using namespace NS_yacco2_terminals; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP signal_guard_start = @/ "#ifndef __%s_h__\n"@/ "#define __%s_h__ 1"; int x = sprintf(big_buf_ ,signal_guard_start ,enum_ph->filename_id()->identifier()->c_str() ,enum_ph->filename_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void gen_Tes_literals_per_spec_voc (std::ofstream& Op_str@/ ,std::vector::iterator I ,std::vector::iterator IE){@/ char big_buf_[BIG_BUFFER_32K]; KCHARP literal = @/ "%s"; for(;I != IE;++I){ T_terminal_def* td = *I; int x = sprintf(big_buf_ ,literal ,td->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void gen_Tes_literals (std::ofstream& Op_str){@/ T_terminals_phrase* pht = O2_T_PHASE; T_rc_phrase* phrc = O2_RC_PHASE; T_error_symbols_phrase* phe = O2_ERROR_PHASE; T_lr1_k_phrase* phlr = O2_LRK_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP t_alphabet = @/ "T-alphabet%s"; int x = sprintf(big_buf_,t_alphabet," "); Op_str.write(big_buf_,x); Op_str<crt_order()->begin(),phlr->crt_order()->end()); gen_Tes_literals_per_spec_voc(Op_str,phrc->crt_order()->begin(),phrc->crt_order()->end()); gen_Tes_literals_per_spec_voc(Op_str,pht->crt_order()->begin(),pht->crt_order()->end()); gen_Tes_literals_per_spec_voc(Op_str,phe->crt_order()->begin(),phe->crt_order()->end()); KCHARP end_t_alphabet = @/ "end-T-alphabet%s"; x = sprintf(big_buf_,end_t_alphabet," "); Op_str.write(big_buf_,x); Op_str<+= void OP_ENUMERATION_HEADER(TOKEN_GAGGLE& Error_queue){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; string fn(enum_ph->filename_id()->identifier()->c_str()); fn += Suffix_enumeration_hdr; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_enum_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*enum_ph->filename_id()); sym->set_who_created("o2externs.w - OP_ENUMERATION_HEADER_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); enumeration_include_guard_for_header(Op_file); Op_file.close(); } @*2 |OP_T_Alphabet| implementation.\fbreak I also included the ``T-alphabet'' file containing the literal names of the Tes for all vocabularies in create order. This is used by \olinker to sprinkle their referenced names throughout the lookahead sets gened by it from the ``xxx.fsc'' file. Why here, cus i use the enumeration file name prefix concatentaed with the ``.fsc'' extension. It only gets gened when meta-terminals or error Tes are to be gened. @+= void OP_T_Alphabet(TOKEN_GAGGLE& Error_queue){@/ T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; string fn_literal(enum_ph->filename_id()->identifier()->c_str()); fn_literal+= Suffix_t_alphabet; std::ofstream Op_file; Op_file.open(fn_literal.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_enum_filename(fn_literal.c_str()); sym->set_line_no_and_pos_in_line(*enum_ph->filename_id()); sym->set_who_created("o2externs.w - OP_ENUMERATION_HEADER_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn_literal.c_str()); gen_Tes_literals(Op_file); Op_file.close(); } @* Template of Errors vocabulary header: |OP_ERRORS_HEADER|.\fbreak Errors vocabulary header generation. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) include files} \INDENT{.1in}{3) Errors include guard declaration} \INDENT{.2in}{3.1) namespace declaration of Errors} \INDENT{.3in}{3.1.1) use terminals enumeration namespace} \INDENT{.3in}{3.1.2) loop thru the Errors list to generate their declarations} \INDENT{.3in}{3.1.2.1) generate the specific Error terminal definition} \INDENT{.3in}{3.1.3) close off namespace definition} \INDENT{.1in}{4) close off Errors include guard declaration} @*3 |errors_loop_thru_and_gen_defs_for_header|. @+= void errors_loop_thru_and_gen_defs_for_header (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; std::vector* dictionary = errors_ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP def_item = @/ " struct %s:public yacco2::CAbs_lr1_sym{\n" "%s"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; SDC_MAP_type* sdc_map = td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_user_declaration); if(j == sdc_map->end()){// shell so gen default KCHARP shell_of_def_item = @/ " struct %s:public yacco2::CAbs_lr1_sym{\n" " %s();\n" " };"; int x = sprintf(big_buf_ ,shell_of_def_item ,td->classsym()->c_str() ,td->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<second; int x = sprintf(big_buf_ ,def_item ,td->classsym()->c_str() ,gw_sdc->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<find(SDC_op); if(j != sdc_map->end()){// grammar writer code Op_str << " op();" << endl; } j = sdc_map->find(SDC_destructor); if(j != sdc_map->end()){// gw code KCHARP dtor = @/ " static void dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P);\n"; int x = sprintf(big_buf_ ,dtor ,td->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_use_enum_namespace_for_header (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP using_T_namespace = @/ " using namespace %s;"; int x = sprintf(big_buf_ ,using_T_namespace ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_namespace_for_header (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP namespace_start = @/ "namespace %s{"; int x = sprintf(big_buf_ ,namespace_start ,errors_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_include_guard_for_header (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP signal_guard_start = @/ "#ifndef __%s_h__\n"@/ "#define __%s_h__ 1"; int x = sprintf(big_buf_ ,signal_guard_start ,errors_ph->filename_id()->identifier()->c_str() ,errors_ph->filename_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_include_files_for_header (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lr_ph = O2_LRK_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_files = @/ "#include \"%s\"\n"@/ "#include \"%s%s\"\n" // T enumeration "#include \"%s%s\"";// lr constants int x = sprintf(big_buf_ ,include_files ,O2_library_file ,enum_ph->filename_id()->identifier()->c_str() ,Suffix_enumeration_hdr ,lr_ph->filename_id()->identifier()->c_str() ,Suffix_LRK_hdr ); Op_str.write(big_buf_,x); Op_str<+= void OP_ERRORS_HEADER(TOKEN_GAGGLE& Error_queue){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; string fn(errors_ph->filename_id()->identifier()->c_str()); fn += Suffix_Errors_hdr; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_errors_hdrfilename(fn.c_str()); sym->set_line_no_and_pos_in_line(*errors_ph->filename_id()); sym->set_who_created("o2externs.w - OP_ERRORS_HEADER_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); errors_include_files_for_header(Op_file); errors_include_guard_for_header(Op_file); Op_file.close(); } @* Template of User Errors vocabulary header: |OP_ERRORS_HEADER|.\fbreak Users Errors vocabulary header generation. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) include files} \INDENT{.1in}{3) use terminals enumeration namespace} \INDENT{.1in}{4) loop thru the T list to generate their implementations} \INDENT{.2in}{4.1) generate the specific T's terminal definition} @*4 |errors_imp_dtor|. @+= void errors_imp_dtor (std::ofstream& Op_str,T_terminal_def* Td,SDC_MAP_ITER_type& J){@/ char big_buf_[BIG_BUFFER_32K]; T_destructor* dtor_t = (T_destructor*)J->second; KCHARP dtor_code = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " bool ABORT_STATUS = ((yacco2::Parser*)P)->top_stack_record()->aborted__;\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; KCHARP dtor_code_noabort = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; std::string::size_type r = dtor_t->syntax_code()->syntax_code()->find("ABORT_STATUS"); int x(0); if(r != std::string::npos){ // using abort status x = sprintf(big_buf_ ,dtor_code ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,dtor_t->syntax_code()->syntax_code()->c_str() ); }else{ x = sprintf(big_buf_ ,dtor_code_noabort ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,dtor_t->syntax_code()->syntax_code()->c_str() ); } Op_str.write(big_buf_,x); Op_str<+= void errors_imp_implementation (std::ofstream& Op_str,T_terminal_def* Td,SDC_MAP_ITER_type& J){@/ char big_buf_[BIG_BUFFER_32K]; T_user_implementation* imp_t = (T_user_implementation*)J->second; Op_str << imp_t->syntax_code()->syntax_code()->c_str(); SDC_MAP_type* sdc_map = Td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_destructor); if(j != sdc_map->end()){ errors_imp_dtor(Op_str,Td,j); } } @*4 |errors_imp_shellimplementation|. @+= void errors_imp_shellimplementation (std::ofstream& Op_str,T_terminal_def* Td,std::string& Autodelete,std::string& Autoabort){@/ char big_buf_[BIG_BUFFER_32K]; KCHARP shell_of_def_item = @/ "%s::\n"@/ "%s()\n"@/ " T_CTOR(\"%s\",T_Enum::T_%s_,%s,%s,%s){}"; int x = sprintf(big_buf_ ,shell_of_def_item ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->t_name()->c_str() // literal ,Td->classsym()->c_str() // enum ,"0"// no dtor ,Autodelete.c_str() ,Autoabort.c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_loop_thru_and_gen_defs_for_imp (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; std::vector* dictionary = errors_ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; string auto_delete; string auto_abort; string dtor_adr; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; SDC_MAP_type* sdc_map = td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_user_implementation); if(td->autodelete()==false){ auto_delete = "false"; }else{ auto_delete = "true"; } if(td->autoabort()==false){ auto_abort = "false"; }else{ auto_abort = "true"; } if(j == sdc_map->end()){// shell so gen default errors_imp_shellimplementation(Op_str,td,auto_delete,auto_abort); }else{// grammar writer defined errors_imp_implementation(Op_str,td,j); } } } @*3 |errors_use_enum_namespace_for_imp|. @+= void errors_use_enum_namespace_for_imp (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP using_namespace = @/ " using namespace %s;\n" " using namespace %s;";// |T_enum| int x = sprintf(big_buf_ ,using_namespace ,errors_ph->namespace_id()->identifier()->c_str() ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void errors_include_files_for_imp (std::ofstream& Op_str){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lr_ph = O2_LRK_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_files = @/ "#include \"%s%s\""; int x = sprintf(big_buf_ ,include_files ,errors_ph->filename_id()->identifier()->c_str() ,Suffix_Errors_hdr ); Op_str.write(big_buf_,x); Op_str<+= void OP_ERRORS_CPP(TOKEN_GAGGLE& Error_queue){@/ T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; string fn(errors_ph->filename_id()->identifier()->c_str()); fn += Suffix_Errors_imp; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_errors_impfilename(fn.c_str()); sym->set_line_no_and_pos_in_line(*errors_ph->filename_id()); sym->set_who_created("o2externs.w - OP_ERRORS_HEADER_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); errors_include_files_for_imp(Op_file); errors_use_enum_namespace_for_imp(Op_file); errors_loop_thru_and_gen_defs_for_imp(Op_file); Op_file.close(); } @* Template of USER T vocabulary header: |OP_USER_T_HEADER|.\fbreak User terminals vocabulary header generation. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) include files} \INDENT{.1in}{3) User T include guard declaration} \INDENT{.2in}{3.1) namespace declaration of Errors} \INDENT{.3in}{3.1.1) use terminals enumeration namespace} \INDENT{.3in}{3.1.2) loop thru the User T list to generate their declarations} \INDENT{.3in}{3.1.2.1) generate the specific User T terminal definition} \INDENT{.3in}{3.1.3) close off namespace definition} \INDENT{.1in}{4) close off User T include guard declaration} @*3 |user_t_loop_thru_and_gen_defs_for_header|. @+= void user_t_loop_thru_and_gen_defs_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; std::vector* dictionary = t_ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; KCHARP def_item = @/ " struct %s:public yacco2::CAbs_lr1_sym{\n" "%s"; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; SDC_MAP_type* sdc_map = td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_user_declaration); if(j == sdc_map->end()){// shell so gen default KCHARP shell_of_def_item = @/ " struct %s:public yacco2::CAbs_lr1_sym{\n" " %s();\n" " };"; int x = sprintf(big_buf_ ,shell_of_def_item ,td->classsym()->c_str() ,td->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<second; int x = sprintf(big_buf_ ,def_item ,td->classsym()->c_str() ,gw_sdc->syntax_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); j = sdc_map->find(SDC_op); if(j != sdc_map->end()){// grammar writer code Op_str << " op();" << endl; } j = sdc_map->find(SDC_destructor); if(j != sdc_map->end()){// gw code KCHARP dtor = @/ " static void dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P);"; int x = sprintf(big_buf_ ,dtor ,td->classsym()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_use_enum_namespace_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP using_T_namespace = @/ " using namespace %s;"; int x = sprintf(big_buf_ ,using_T_namespace ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_terminals_refs_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; if(t_ph->terminals_refs_code() != 0){ KCHARP t_ref_code = @/ " %s"; int x = sprintf(big_buf_ ,t_ref_code ,t_ph->terminals_refs_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_terminals_sufx_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; if(t_ph->terminals_sufx_code() != 0){ KCHARP t_sufx_code = @/ " %s"; int x = sprintf(big_buf_ ,t_sufx_code ,t_ph->terminals_sufx_code()->syntax_code()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_namespace_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP namespace_start = @/ "namespace %s{"; int x = sprintf(big_buf_ ,namespace_start ,t_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_include_guard_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP signal_guard_start = @/ "#ifndef __%s_h__\n"@/ "#define __%s_h__ 1"; int x = sprintf(big_buf_ ,signal_guard_start ,t_ph->filename_id()->identifier()->c_str() ,t_ph->filename_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_include_files_for_header (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lr_ph = O2_LRK_PHASE; T_error_symbols_phrase* errors_ph = O2_ERROR_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_files = @/ "#include \"%s\"\n"@/ "#include \"%s%s\"\n" // T enumeration "#include \"%s%s\"\n"// lr constants "#include \"%s%s\"";// errors int x = sprintf(big_buf_ ,include_files ,O2_library_file ,enum_ph->filename_id()->identifier()->c_str() ,Suffix_enumeration_hdr ,lr_ph->filename_id()->identifier()->c_str() ,Suffix_LRK_hdr ,errors_ph->filename_id()->identifier()->c_str() ,Suffix_Errors_hdr ); Op_str.write(big_buf_,x); Op_str<+= void OP_USER_T_HEADER(TOKEN_GAGGLE& Error_queue){@/ T_terminals_phrase* t_ph = O2_T_PHASE; string fn(t_ph->filename_id()->identifier()->c_str()); fn += Suffix_T_hdr; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_errors_hdrfilename(fn.c_str()); sym->set_line_no_and_pos_in_line(*t_ph->filename_id()); sym->set_who_created("o2externs.w - OP_USER_T_HEADER_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); user_t_include_files_for_header(Op_file); user_t_include_guard_for_header(Op_file); Op_file.close(); } @* Template of USER T vocabulary header: |OP_USER_T_HEADER|.\fbreak Errors vocabulary header generation. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) include files} \INDENT{.1in}{3) use terminals enumeration namespace} \INDENT{.1in}{4) terminals-refs code} \INDENT{.1in}{5) loop thru the USer T list to generate their implementations} \INDENT{.2in}{5.1) generate the specific User T terminal definition} \INDENT{.1in}{6) terminals-sufx code} @*4 |user_t_imp_dtor|. @+= void user_t_imp_dtor (std::ofstream& Op_str,T_terminal_def* Td,SDC_MAP_ITER_type& J){@/ char big_buf_[BIG_BUFFER_32K]; T_destructor* dtor_t = (T_destructor*)J->second; KCHARP dtor_code = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " bool ABORT_STATUS = ((yacco2::Parser*)P)->top_stack_record()->aborted__;\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; KCHARP dtor_code_noabort = @/ "void %s::dtor_%s(yacco2::VOIDP This,yacco2::VOIDP P){\n"@/ " %s* R = (%s*)(This);\n"@/ " %s\n"@/ "}"; std::string::size_type r = dtor_t->syntax_code()->syntax_code()->find("ABORT_STATUS"); int x(0); if(r != std::string::npos){ // using abort status x = sprintf(big_buf_ ,dtor_code ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,dtor_t->syntax_code()->syntax_code()->c_str() ); }else{ x = sprintf(big_buf_ ,dtor_code_noabort ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,dtor_t->syntax_code()->syntax_code()->c_str() ); } Op_str.write(big_buf_,x); Op_str<+= void user_t_imp_implementation (std::ofstream& Op_str,T_terminal_def* Td,SDC_MAP_ITER_type& J){@/ char big_buf_[BIG_BUFFER_32K]; T_user_implementation* imp_t = (T_user_implementation*)J->second; Op_str << imp_t->syntax_code()->syntax_code()->c_str(); SDC_MAP_type* sdc_map = Td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_destructor); if(j != sdc_map->end()){ errors_imp_dtor(Op_str,Td,j); } } @*4 |user_t_imp_shellimplementation|. @+= void user_t_imp_shellimplementation (std::ofstream& Op_str,T_terminal_def* Td,std::string& Autodelete,std::string& Autoabort){@/ char big_buf_[BIG_BUFFER_32K]; KCHARP shell_of_def_item = @/ "%s::\n"@/ "%s()\n"@/ " T_CTOR(\"%s\",T_Enum::T_%s_,%s,%s,%s){}"; int x = sprintf(big_buf_ ,shell_of_def_item ,Td->classsym()->c_str() ,Td->classsym()->c_str() ,Td->t_name()->c_str() // literal ,Td->classsym()->c_str() // enum ,"0"// no dtor ,Autodelete.c_str() ,Autoabort.c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_loop_thru_and_gen_defs_for_imp (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; std::vector* dictionary = t_ph->crt_order(); char big_buf_[BIG_BUFFER_32K]; string auto_delete; string auto_abort; string dtor_adr; std::vector::iterator i = dictionary->begin(); std::vector::iterator ie = dictionary->end(); for(;i != ie;++i){ T_terminal_def* td = *i; SDC_MAP_type* sdc_map = td->directives_map(); SDC_MAP_ITER_type j = sdc_map->find(SDC_user_implementation); if(td->autodelete()==false){ auto_delete = "false"; }else{ auto_delete = "true"; } if(td->autoabort()==false){ auto_abort = "false"; }else{ auto_abort = "true"; } if(j == sdc_map->end()){// shell so gen default user_t_imp_shellimplementation(Op_str,td,auto_delete,auto_abort); }else{// grammar writer defined user_t_imp_implementation(Op_str,td,j); } } } @*3 |user_t_use_enum_namespace_for_imp|. @+= void user_t_use_enum_namespace_for_imp (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP using_namespace = @/ " using namespace %s;\n" " using namespace %s;";// |T_enum| int x = sprintf(big_buf_ ,using_namespace ,t_ph->namespace_id()->identifier()->c_str() ,enum_ph->namespace_id()->identifier()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void user_t_include_files_for_imp (std::ofstream& Op_str){@/ T_terminals_phrase* t_ph = O2_T_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_lr1_k_phrase* lr_ph = O2_LRK_PHASE; char big_buf_[BIG_BUFFER_32K]; KCHARP include_files = @/ "#include \"%s%s\""; int x = sprintf(big_buf_ ,include_files ,t_ph->filename_id()->identifier()->c_str() ,Suffix_T_hdr ); Op_str.write(big_buf_,x); Op_str<+= void OP_USER_T_CPP(TOKEN_GAGGLE& Error_queue){@/ T_terminals_phrase* t_ph = O2_T_PHASE; string fn(t_ph->filename_id()->identifier()->c_str()); fn += Suffix_Errors_imp; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_errors_impfilename(fn.c_str()); sym->set_line_no_and_pos_in_line(*t_ph->filename_id()); sym->set_who_created("o2externs.w - OP_USER_T_CPP",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); user_t_include_files_for_imp(Op_file); user_t_use_enum_namespace_for_imp(Op_file); user_t_loop_thru_and_gen_defs_for_imp(Op_file); Op_file.close(); } @* Template of FSC File: |OP_FSC_FILE|.\fbreak File of grammar's first threads info for \o2{}linker. \fbreak \INDENT{.1in}{1) Comments --- file name, time and date info} \INDENT{.1in}{2) fsc prelude} \INDENT{.1in}{3) list of native Tes in grammar's first set} \INDENT{.1in}{4) list of directly called threads in its first set} \INDENT{.1in}{5) complete list of used threads in grammar for linker document} \INDENT{.1in}{6) grammar's comments for linker document} @*3 |fsc_prelude_imp|. @+= void fsc_prelude_imp (std::ofstream& Op_str,std::string& Fn){@/ KCHARP pp_thd_nm(0); string transitive("n"); string mono; char big_buf_[BIG_BUFFER_32K]; T_fsm_phrase* fcp = O2_FSM_PHASE; T_parallel_parser_phrase* ppp = O2_PP_PHASE; T_enum_phrase* enum_ph = O2_T_ENUM_PHASE; T_rules_phrase* rules_ph = O2_RULES_PHASE; RULE_DEFS_TBL_ITER_type i= rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie= rules_ph->crt_order()->end(); rule_def*rd= *i; FIRST_SET_type* fs = rd->first_set(); FIRST_SET_ITER_type j = fs->begin(); FIRST_SET_ITER_type je = fs->end(); for(;j!=je;++j){ T_in_stbl* tintbl = *j; T_terminal_def* tdef = tintbl->t_def(); if(tdef->enum_id() == LR1_PARALLEL_OPERATOR){ transitive[0] = 'y'; break; } } KCHARP fsc_prelude = "transitive %s\n" "grammar-name \"%s\"\n" "name-space \"%s\"\n" "thread-name \"%s\"\n" "monolithic %s\n" "file-name \"%s\"\n" "no-of-T %i"; if(ppp == 0){ pp_thd_nm = fcp->fsm_class_phrase()->identifier()->identifier()->c_str(); mono += 'y'; }else{ pp_thd_nm = ppp->pp_funct()->identifier()->identifier()->c_str(); mono += 'n'; } int x = sprintf(big_buf_ ,fsc_prelude ,transitive.c_str() ,fcp->filename_id()->identifier()->c_str() ,fcp->namespace_id()->identifier()->c_str() ,pp_thd_nm ,mono.c_str() ,Fn.c_str() ,enum_ph->total_enumerate() ); Op_str.write(big_buf_,x); Op_str<+= void list_of_native_Tes_in_fs_imp (std::ofstream& Op_str){@/ char big_buf_[BIG_BUFFER_32K]; int no_Tes_in_list(0); string Tes_in_list; STATES_ITER_type si = LR1_STATES.begin(); state* cur_state = *si; int no_of_Tes_in_fs(0); std::set fs_set; T_rules_phrase* rules_ph = O2_RULES_PHASE; RULE_DEFS_TBL_ITER_type i= rules_ph->crt_order()->begin(); RULE_DEFS_TBL_ITER_type ie= rules_ph->crt_order()->end(); rule_def*rd= *i; FIRST_SET_type* fs = rd->first_set(); FIRST_SET_ITER_type j = fs->begin(); FIRST_SET_ITER_type je = fs->end(); for(;j!=je;++j){ T_in_stbl* tintbl = *j; T_terminal_def* tdef = tintbl->t_def(); if(tdef->enum_id() == LR1_PARALLEL_OPERATOR){ continue; } ++no_Tes_in_list; } done:; KCHARP fs_list_of_Tes = "list-of-native-first-set-terminals %i"; KCHARP fs_T_in_list = " %s"; KCHARP end_fs_list_of_Tes = "end-list-of-native-first-set-terminals%s"; int x = sprintf(big_buf_ ,fs_list_of_Tes ,no_Tes_in_list ); Op_str.write(big_buf_,x); Op_str<begin(); for(;j!=je;++j){ T_in_stbl* tintbl = *j; T_terminal_def* tdef = tintbl->t_def(); if(tdef->enum_id() == LR1_PARALLEL_OPERATOR){ continue; } if(tdef->enum_id() == LR1_INVISIBLE_SHIFT_OPERATOR){// handled by \olinker x = sprintf(big_buf_ ,fs_T_in_list ,LR1_FSET_TRANSIENCE_OPERATOR_LITERAL ); }else{ x = sprintf(big_buf_ ,fs_T_in_list ,tdef->classsym()->c_str() ); } Op_str.write(big_buf_,x); Op_str<+= void transitive_list_of_threads_in_fs_imp (std::ofstream& Op_str){@/ char big_buf_[BIG_BUFFER_32K]; T_rules_phrase* rules_ph = O2_RULES_PHASE; AST* rules_tree = rules_ph->phrase_tree(); AST* start_rule_def_t= AST::get_1st_son(*rules_tree); rule_def* rd = (rule_def*)AST::content(*start_rule_def_t); int no_threads_in_list = rd->called_thread_first_set()->size(); KCHARP transitive_thread_list = "list-of-transitive-threads %i"; KCHARP transitive_thread_in_list = " %s::%s"; KCHARP end_transitive_thread_list = "end-list-of-transitive-threads%s"; int x = sprintf(big_buf_,transitive_thread_list,no_threads_in_list); Op_str.write(big_buf_,x); Op_str<::iterator e = rd->called_thread_first_set()->begin(); std::set::iterator ee = rd->called_thread_first_set()->end(); for(;e!=ee;++e){ T_called_thread_eosubrule* called_thd = *e; x = sprintf(big_buf_ ,transitive_thread_in_list ,called_thd->ns()->identifier()->c_str() ,called_thd->called_thread_name()->identifier()->c_str()); Op_str.write(big_buf_,x); Op_str<+= void used_list_of_threads_imp (std::ofstream& Op_str){@/ char big_buf_[BIG_BUFFER_32K]; std::set used_threads; STATES_ITER_type i = LR1_STATES.begin(); STATES_ITER_type ie = LR1_STATES.end(); for(;i!=ie;++i){// extract all the used threads state* start_state = *i; S_VECTOR_ELEMS_type* state_elems_wparallelism(0); S_VECTORS_type& vect = start_state->state_s_vector_; S_VECTORS_ITER_type vi = vect.find(LR1_PARALLEL_OPERATOR); if(vi == vect.end()) continue; //no parallelism in state? state_elems_wparallelism = &vi->second; S_VECTOR_ELEMS_ITER_type k = state_elems_wparallelism->begin(); S_VECTOR_ELEMS_ITER_type ke = state_elems_wparallelism->end(); for(;k!=ke;++k){ state_element* thread_1st_elem = *k;// parallel operator AST* rtned_T_t = thread_1st_elem->next_state_element_->sr_element_; AST* bypassed_thd_eos_t = AST::brother(*rtned_T_t); if(bypassed_thd_eos_t == 0) continue; CAbs_lr1_sym* sym = AST::content(*bypassed_thd_eos_t); if(sym->enumerated_id__ != T_Enum::T_T_called_thread_eosubrule_){ continue; } T_called_thread_eosubrule* called_thd = (T_called_thread_eosubrule*)sym; // add to used thread if first time string fqtnm; fqtnm += called_thd->ns()->identifier()->c_str(); fqtnm += "::"; fqtnm += called_thd->called_thread_name()->identifier()->c_str(); if(used_threads.find(fqtnm) == used_threads.end()) used_threads.insert(fqtnm); } } KCHARP used_thread_list = "list-of-used-threads %i"; KCHARP used_thread_in_list = " %s"; KCHARP end_used_thread_list = "end-list-of-used-threads%s"; int x = sprintf(big_buf_,used_thread_list,used_threads.size()); Op_str.write(big_buf_,x); Op_str<::iterator si = used_threads.begin(); std::set::iterator sie = used_threads.end(); for(;si!=sie;++si){ x = sprintf(big_buf_ ,used_thread_in_list ,si->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void grammar_s_comments_for_linker_doc_imp (std::ofstream& Op_str){@/ char big_buf_[BIG_BUFFER_32K]; T_fsm_phrase* fcp = O2_FSM_PHASE; KCHARP fsm_comments = "fsm-comments\n" "\"%s\""; int x = sprintf (big_buf_ ,fsm_comments ,fcp->comment()->c_string()->c_str() ); Op_str.write(big_buf_,x); Op_str<+= void OP_FSC_FILE(TOKEN_GAGGLE& Error_queue){@/ T_fsm_phrase* fsm_ph = O2_FSM_PHASE; string fn(fsm_ph->filename_id()->identifier()->c_str()); fn += Suffix_fsc; std::ofstream Op_file; Op_file.open(fn.c_str(),ios_base::out|ios::trunc); if(!Op_file){ CAbs_lr1_sym* sym = new Err_bad_fsmtbl_filename(fn.c_str()); sym->set_line_no_and_pos_in_line(*fsm_ph->filename_id()); sym->set_who_created("o2externs.w - OP_FSC_FILE",__LINE__); Error_queue.push_back(*sym); return; } intro_comment(Op_file,fn.c_str()); fsc_prelude_imp(Op_file,fn); list_of_native_Tes_in_fs_imp(Op_file); transitive_list_of_threads_in_fs_imp(Op_file); used_list_of_threads_imp(Op_file); grammar_s_comments_for_linker_doc_imp(Op_file); Op_file.close(); } @** Bric-a-brac.\fbreak @ Better coordination between the coordinates.\fbreak To improve processing of nested files, a global |yacco2::STK_FILE_NOS__| was introduced to swing-and-sway with its partner |yacco2::FILE_CNT__|. |yacco2::FILE_CNT__| gets incremented by the |tok_can| container while |yacco2::STK_FILE_NOS__| pushes and pops |yacco2::FILE_CNT__|. The global |yacco2::FILE_TBL__| keeps an array of files processed. The importance of |yacco2::STK_FILE_NOS__| and |yacco2::FILE_CNT__| is in the assignment of the source file coordinates to the manufactured tokens. This allows one to correlate the error token back to the source file. As |yacco2::FILE_CNT__| is an expanding number, the grammar writer must properly coordinate when nested files are occurring, and to ensure when the file gets closed that the calling file number becomes |yacco2::STK_FILE_NOS__| current value. A little stacking sir...\fbreak \fbreak Improvement: 8 Mar. 2005 @ Blending of Yacco2 and Linker externals.\fbreak General house cleaning. A one-stop-lollipop-stall of recycled ideas: your marche puce. Look at the stalls ... folders. Externals folder is now equal to Linker, Yacco2, Library. Ahem...\fbreak \fbreak Improvement: 9 Mar. 2005 @ Remove option /s:xxxx number of source lines to generate.\fbreak This was a limitation of Microsoft's c++ compiler circa release 5.xx. It honked. Now I'm more optimistic of their current products. So I removed the /s:xxxx option from the |Yacco2_lcl_options(s).lex| grammars and as a passed in parameter to |YACCO2_PARSE_CMD_LINE|. Ah the signs of spring cleaning...\fbreak \fbreak Improvement: 2 June 2005 @ Bug in MS compiler 2005.\fbreak To make my |mpost_output| grammar more print presentable when emitting itself for |mpost|, |cweave|, and |pdftex| consumption, i moved most of its class code into static class procedures whereby their implementations are within this document. Part of the improvement was the calling of the static member |MPOST_CWEB_EMIT_PREFIX_CODE| after |ctor| creation from the class's ``op'' directive to do basic table initialization and to establish the output files to be written to. The class object's ``this'' was passed as a parameter to |MPOST_CWEB_EMIT_PREFIX_CODE|.\fbreak Well the bug, |@| within |MPOST_CWEB_EMIT_PREFIX_CODE| established opening the files. After verifying that the file's ``ofstream'' was healthy, upon the 1st attempt to write to it MS c++ emitted code honked from its mutex handling routine. Well at least they are improving in their trappings.\fbreak Work around, extract from the |MPOST_CWEB_EMIT_PREFIX_CODE| procedure the initialization of the class's variables and place it in the ``op'' directive of the class before the call to |MPOST_CWEB_EMIT_PREFIX_CODE| to complete the balance of work. Now MS's emitted code doesn't honk when the initialization context is within the class code.\fbreak \fbreak\fbreak Grrr: 26 Jan. 2006 \fbreak @ Initialize the |Cmpost_output| class variables. The initial |push_back| of ``0'' is to register a boggy 0 element as i count from 1. @= Fsm->no_subrules_per_rule_.push_back(0); Fsm->gened_date_time_ += DATE_AND_TIME(); Fsm->mp_dimension_ += " abcdefghijklmnopqrstuvwxyz"; Fsm->w_fig_no_ = 0; Fsm->rule_def_ = 0; Fsm->subrule_def_ = 0; Fsm->rule_no_ = 0; Fsm->subrule_no_ = 0; Fsm->elem_no_ = 0; Fsm->no_of_rules_ = 0; Fsm->no_of_subrules_ = 0; Fsm->mp_filename_ += Fsm->grammar_filename_prefix_.c_str(); Fsm->mp_filename_ += ".mp"; Fsm->omp_file_.open(Fsm->mp_filename_.c_str(),ios_base::out|ios::trunc); if(!Fsm->omp_file_){ CAbs_lr1_sym* sym = new Err_bad_filename(Fsm->mp_filename_.c_str()); Fsm->parser__->add_token_to_error_queue(*sym); Fsm->parser__->set_stop_parse(true); return; } Fsm->w_filename_ += Fsm->grammar_filename_prefix_.c_str(); Fsm->w_filename_ += ".w"; Fsm->ow_file_.open(Fsm->w_filename_.c_str(),ios_base::out|ios::trunc); if(!Fsm->ow_file_){ CAbs_lr1_sym* sym = new Err_bad_filename(Fsm->w_filename_.c_str()); Fsm->parser__->add_token_to_error_queue(*sym); Fsm->parser__->set_stop_parse(true); return; } @ |convertMPtoPDF| macro bug.\fbreak Well here's the scoop: my grammar diagrams are smoking but when there is a blank (space) in a terminal being displayed, the |convertMPtoPDF| macro halts saying that the |empty| macro is illegally terminated. Alas, for the moment i substitute a ``.'' in place of the space to keep the pictures flowing.\fbreak \fbreak 3 Feb. 2006 @ |cweave| emits bad cross-reference command in o2externs.scn file.\fbreak It emits:\fbreak $\backslash${}I$\backslash${}X8, xx , yy, 73:accrue source for emit$\backslash${}X\fbreak $\backslash${}U7.\fbreak instead of:\fbreak $\backslash${}I$\backslash${}X8:accrue source for emit$\backslash${}X\fbreak $\backslash${}Us10, 11, ..., 70$\backslash${}ETs72. This works. But checking with other modules like ``wlibrary'' the code is right so what's making it cough? \fbreak \fbreak Until i fix |cweave| either remove ``\\ind'' line at the end of ``o2externs.tex'' file or ``remove or add'' the offending commands in ``o2externs.scn'' file. \fbreak \fbreak 14 June 2006 @ |cweave| emits tab in place of ``open brace''.\fbreak The ``ctangle'' emitted cpp code is okay. Shows up when i emit ``cpp'' source code implementation procedures for the grammar. \fbreak \fbreak 12 Nov 2006 @ Language cleanup.\fbreak Removed ``entry and exit'' routines from fsm class definition. Fsm's ctor and dtor allow the same points to be tweaked. In \O2's creation days, i was over zealous on trigger points --- fsm, push and pop action off the parse stack, and subrules being separate classes. all this to leave tracks when needed.\fbreak Eliminated the subrule directives as they were needed only when a subrule was designed as a separate class --- now a subrule definition is part of the rule definition: srx() where x is the subrule number. Separate subrule definitions really had marginal utility --- euphemism for useless and never exploited.\fbreak \INDENT{.2in}{1) rhs-op replaced by op directive} \INDENT{.2in}{2) rhs-xxx elinimated directives} \INDENT{.3in}{ where xxx is constructor, destructor, user-declaration or implementation} This my take on gently waff where whiffes linger... \fbreak \fbreak 13 Nov 2006 @ Templates and their tantrums.\fbreak Due to MS's equal algorithm throwing up, i roll my own set compare --- |find_common_la_set_idx|. \fbreak \fbreak 20 Nov 2006 @ Rule use count off by nested recursion.\fbreak The rule use count algorithm didn't handle the following boiled down example properly:\fbreak \ptindent{T $\rightarrow$ Ra T Rb} \ptindent{T $\rightarrow$ Ra ...} The nested T was recognized but not post evaluated on Ra use. It should double its maximun calculated use count after direct and indirect rule calculations.\fbreak \fbreak Nov. 2007 @** Index.