summaryrefslogtreecommitdiff
path: root/web/yacco2/o2linker/prog.w
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/yacco2/o2linker/prog.w
Initial commit
Diffstat (limited to 'web/yacco2/o2linker/prog.w')
-rw-r--r--web/yacco2/o2linker/prog.w1134
1 files changed, 1134 insertions, 0 deletions
diff --git a/web/yacco2/o2linker/prog.w b/web/yacco2/o2linker/prog.w
new file mode 100644
index 0000000000..38c728960c
--- /dev/null
+++ b/web/yacco2/o2linker/prog.w
@@ -0,0 +1,1134 @@
+@q file: prog.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% */@>
+@*2 External routines and globals.\fbreak
+General routines to get things going:\fbreak
+\ptindent{1) get control file and put into Linker's holding file}
+\ptindent{2) parse the command line}
+\ptindent{3) format errors}
+These are defined by including |o2_externs.h|.
+
+The globals are:\fbreak
+\ptindent{1) |Error_queue| --- global container of errors passed across all parsings}
+\ptindent{2) |GRAMMAR_DICTIONARY| --- thread container whose contents point into the symbol table}
+\ptindent{3) |T_DICTIONARY| --- terminal container whose contents point into the symbol table}
+\ptindent{4) |T_THREAD_ID_LIST| --- thread id list per terminal}
+\ptindent{5) |NO_OF_THREADS| --- found number of threads}
+\ptindent{6) |NO_WORDS_FOR_BIT_MAP| --- calculated number of words per thread bit map}
+\ptindent{7) |THREAD_ID_FS| --- first set per thread id}
+
+@<External rtns and ...@>=
+extern int NO_OF_THREADS;@/
+extern int NO_WORDS_FOR_BIT_MAP;
+
+@ Do we have errors?.
+Check that error queue for those errors.
+Note, |DUMP_ERROR_QUEUE| will also flush out
+ any launched threads for the good housekeeping or is it
+housetrained seal
+award? Trying to do my best in the realm of short lived winddowns.
+@<if error queue not empty then deal with posted errors@>=
+if(Error_queue.empty()!=true){
+ DUMP_ERROR_QUEUE(Error_queue);
+ return 1;
+}
+
+@** Local routines.\fbreak
+@*2 Parse linker control file.\fbreak
+Handcrafted file that gathers all the grammar control files together for the processing.
+Note the use of |tok_can<std::ifstream>| to read raw characters
+and produce tokens in just-in-time.
+@<parse linker control file@>=
+ cout << "Parse linker control file " << endl;
+ using namespace NS_linker_pass3;
+ tok_can<std::ifstream> cntl_file_tokens(cntl_file_name.c_str());
+ TOKEN_GAGGLE P3_tokens;
+ Clinker_pass3 linker_cntl_file_fsm;
+ Parser linker_cntl_file(linker_cntl_file_fsm,&cntl_file_tokens,&P3_tokens,0,&Error_queue,0,0);
+ linker_cntl_file.parse();
+ @<if error queue not empty then deal with posted errors@>;
+
+@*2 Parse T alphabet.\fbreak
+Ahh the terminal vocabulary. The symbol's position within the list
+is also its enumerate value starting from 0.
+The |T_alphabet| grammar places the terminal symbols into the symbol table
+as a 1:1 mapping, into
+the |T_DICTIONARY| as a consolidated repository
+and into the |T_THREAD_ID_LIST|
+which is the terminal to thread list relationship used
+to quickly determine if the current token has the potential to run as a thread.
+Each thread contains its first set list squirreled away in thread\_attributes's |list_of_Ts_|.
+
+@<parse T alphabet@>=
+cout << "Parse alphabet" << endl;
+using namespace NS_link_cleanser;
+tok_can<std::ifstream> T_file_tokens(linker_cntl_file_fsm.t_alphabet_filename_.c_str());
+TOKEN_GAGGLE cleanser_tokens;
+Clink_cleanser cleanser_fsm;
+Parser cleanser(cleanser_fsm,&T_file_tokens,&cleanser_tokens,0,&Error_queue,0,0);
+cleanser.parse();
+
+using namespace NS_t_alphabet;
+TOKEN_GAGGLE T_tokens;
+Ct_alphabet T_fsm;
+Parser T_pass3(T_fsm,&cleanser_tokens,&T_tokens,0,&Error_queue,0,0);
+T_pass3.parse();
+@<if error queue not empty then deal with posted errors@>;
+
+@*2 Parse fsc files.\fbreak
+Digest those fsc files. Burp. Better that than the other end.
+Now for a chiro for the stretched stomach muscles.
+The |fsc_file| grammar puts the digested grammar's fsc content into
+the symbol table of thread attribute's object and its reference into
+the |GRAMMAR_DICTIONARY|.
+@<parse fsc files@>=
+cout << "Parse fsc files" << endl;
+TOKEN_GAGGLE cleanser_fsc_tokens;
+TOKEN_GAGGLE fsc_file_output_tokens;
+std::vector<std::string>::iterator ii = linker_cntl_file_fsm.grammars_fsc_files_.begin();
+std::vector<std::string>::iterator iie = linker_cntl_file_fsm.grammars_fsc_files_.end();
+for(;ii!=iie;++ii){
+ tok_can<std::ifstream> T_fsc_file_tokens(ii->c_str());
+ Clink_cleanser cleanser_fsc;
+ Parser fsc_cleanser(cleanser_fsc,&T_fsc_file_tokens
+ ,&cleanser_fsc_tokens,0,&Error_queue,0,0);
+ fsc_cleanser.parse();
+ @<if error queue not empty then deal with posted errors@>;
+
+ using namespace NS_fsc_file;
+ Cfsc_file fsc_file_fsm;
+ Parser fsc_file_pass(fsc_file_fsm,&cleanser_fsc_tokens
+ ,&fsc_file_output_tokens,0,&Error_queue,0,0);
+ fsc_file_pass.parse();
+ @<if error queue not empty then deal with posted errors@>;
+ cleanser_fsc_tokens.clear();
+ fsc_file_output_tokens.clear();
+}
+ @<if error queue not empty then deal with posted errors@>;
+
+@*2 |load_linkkw_into_tbl|.\fbreak
+ These are the keywords
+ from all the languages to be parsed.
+ So why the loading up of keywords? Speed.
+ |linker_id| does a symbol table lookup for \olinker.
+ So where ever appropriate like the
+ ``first set control'' files, normal boundary parsing can take place.
+ There is your lexical grammar that discriminates characters into
+ tokens like keywords, comments followed by
+ a separate syntax grammar to process the language structure.
+ See comments in Yacco2's external document regarding the reason for
+ the kludge.
+@<accrue linker code@>+=
+void load_linkkw_into_tbl(yacco2::CAbs_lr1_sym* Kw){
+ using namespace yacco2_stbl;
+ T_sym_tbl_report_card report_card;
+ KCHARP 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_);
+}
+
+void load_linkkws_into_tbl()@/
+{
+ cout << "Load linker's keywords " << endl;
+ load_linkkw_into_tbl(new T_transitive);
+ load_linkkw_into_tbl(new T_grammar_name);
+ load_linkkw_into_tbl(new T_name_space);
+ load_linkkw_into_tbl(new T_thread_name);
+ load_linkkw_into_tbl(new T_fsm_comments);
+ load_linkkw_into_tbl(new T_monolithic);
+ load_linkkw_into_tbl(new T_file_name);
+ load_linkkw_into_tbl(new T_no_of_T);
+ load_linkkw_into_tbl(new T_list_of_native_first_set_terminals);
+ load_linkkw_into_tbl(new T_end_list_of_native_first_set_terminals);
+ load_linkkw_into_tbl(new T_list_of_transitive_threads);
+ load_linkkw_into_tbl(new T_end_list_of_transitive_threads);
+ load_linkkw_into_tbl(new T_list_of_used_threads);
+ load_linkkw_into_tbl(new T_end_list_of_used_threads);
+ load_linkkw_into_tbl(new T_T_alphabet);
+ load_linkkw_into_tbl(new T_end_T_alphabet);
+ load_linkkw_into_tbl(new T_file_of_T_alphabet);
+ load_linkkw_into_tbl(new T_emitfile);
+ load_linkkw_into_tbl(new T_preamble);
+ load_linkkw_into_tbl(new T_end_preamble);
+}
+
+
+@*2 Verify that all threads used are defined.\fbreak
+ Simple check! Just
+sequentially read the |GRAMMAR_DICTIONARY| whose elements are pointers to their
+symbol table registry where the entry is not ``defined'' but referenced by some
+transitive call list.
+The loop just pours the rogues into the error queue and uses the
+token co-ordinates that created the entry as ``used'' for the error message.
+This allows the error dump to pinpoint the source file and specific line that
+referenced the rogue thread.
+Correction is to add the missing grammar to the control file list or
+correct the grammar that issued the thread call.
+ @<post verify that there are no threads ``used'' and not ``defined''@>=
+ std::vector<table_entry*>::iterator th_i = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator th_ie = GRAMMAR_DICTIONARY.end();@/
+ for(;th_i != th_ie;++th_i){
+ table_entry* tbl_entry = *th_i;
+
+ if(tbl_entry->defined_ == true) continue;
+ CAbs_lr1_sym* sym = new Err_bad_th_in_list;
+ sym->set_who_created("linker.w",__LINE__);
+
+ @=thread_attributes*th_goodies= ((th_in_stbl*)tbl_entry->symbol_)->thread_in_stbl();@>@/
+ sym->set_rc(*th_goodies);
+ Error_queue.push_back(*sym);
+ }
+ @<if error queue not empty then deal with posted errors@>;
+
+@** Sort thread dictionary.\fbreak
+The grammars are divided into 2 types:\fbreak
+\ptindent{1) monolithic --- stand alone grammars}
+\ptindent{2) called threads}
+
+
+To facilitate the emitted code,
+the following partial order is imposed on the fully qualified name (FQN) of the grammar
+composed of the grammar's namespace
+and name separated by ``::'': for example, |NS_eol::TH_eol|. This is the calling handle of
+the thread when used with the ${\vert\vert\vert}$ statement.
+The partial order is divided into 2 parts --- threads followed by standalone
+grammars:\fbreak
+\ptindent{thread vs thread --- lexicographical order on ``thread name'' only}
+\ptindent{thread vs mono --- thread less than mono grammar}
+\ptindent{mono vs thread --- thread less than mono grammar}
+\ptindent{mono vs mono --- lexicographical order on FQN}
+The order defines explicitly the enumerate value assigned to each thread starting from 0.
+The standalone grammars (monolithic) are not part of the thread stable that gets emitted.
+@<accrue linker code@>+=
+bool sort_threads_criteria(const table_entry* P1,const table_entry* P2){
+ @=th_in_stbl*th_tbl1= (th_in_stbl*)P1->symbol_;@> @/
+ @=th_in_stbl*th_tbl2= (th_in_stbl*)P2->symbol_;@> @/
+ thread_attributes* p1 = th_tbl1->thread_in_stbl();
+ thread_attributes* p2 = th_tbl2->thread_in_stbl();
+ int len_a = p1->thread_name_->c_string()->size();
+ int len_b = p2->thread_name_->c_string()->size();
+ string ucase_a;
+ for(int x=0;x<len_a;++x){
+ ucase_a += toupper((*p1->thread_name_->c_string())[x]) ;
+ }
+ string ucase_b;
+ for(int x=0;x<len_b;++x){
+ ucase_b += toupper((*p2->thread_name_->c_string())[x]) ;
+ }
+ if(len_a < len_b){
+ for(int x = len_a+1;x <=len_b;++x) ucase_a += ' ';
+ }else{
+ for(int x = len_b+1;x <=len_a;++x) ucase_b += ' ';
+ }
+ int result;
+ if(p1->monolithic_ == 'n'){// a:thread
+ if(p2->monolithic_ == 'n'){// a:thread b:thread
+ result = strcmp(ucase_a.c_str(),ucase_b.c_str());
+ if(result < 0) return true;// a less b
+ return false;// a gt b
+ }
+ return true;// a:thread b:mono; a less b
+ }
+ if(p2->monolithic_ == 'n'){// a:mono b:thread
+ return false;// a gt b
+ }
+ // a:mono b:mono changed to the thread name instead of fqn
+ int len_fqna = p1->thread_name_->c_string()->size();
+ int len_fqnb = p2->thread_name_->c_string()->size();
+ string ucase_fqna;
+ for(int x=0;x<len_fqna;++x){
+ ucase_fqna += toupper((*p1->thread_name_->c_string())[x]);
+ }
+ string ucase_fqnb;
+ for(int x=0;x<len_fqnb;++x){
+ ucase_fqnb += toupper((*p2->thread_name_->c_string())[x]);
+ }
+ if(len_fqna < len_fqnb){
+ for(int x = len_fqna+1;x <=len_fqnb;++x) ucase_fqna += ' ';
+ }else{
+ for(int x = len_fqnb+1;x <=len_fqna;++x) ucase_fqnb += ' ';
+ }
+ result = strcmp(ucase_fqna.c_str(),ucase_fqnb.c_str());
+ if(result < 0 ) return true;// a less b
+ return false;// a gt b
+}
+
+@*2 Sort uses template algorithm.
+@<sort thread dictionary@>=
+ cout << "Sort thread dictionary" << endl;
+ stable_sort(GRAMMAR_DICTIONARY.begin(),GRAMMAR_DICTIONARY.end(),sort_threads_criteria);
+
+@*2 Dump sorted dictionary.\fbreak
+Not another reality show? Yupp.
+@<dump sorted dictionary@>=
+yacco2::lrclog << "Sorted thread dictionary" << GRAMMAR_DICTIONARY.size()<< std::endl;
+ std::vector<table_entry*>::iterator dth_i = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator dth_ie = GRAMMAR_DICTIONARY.end();@/
+ int pos(-1);
+ for(;dth_i != dth_ie;++dth_i){
+ ++pos;
+ table_entry* tbl_entry = *dth_i;
+ @=thread_attributes*th_goodies= ((th_in_stbl*)tbl_entry->symbol_)->thread_in_stbl();@>@/
+yacco2::lrclog << "tbl entry*: "
+<< tbl_entry << " th_goodies*: " << th_goodies
+<< " " << pos << ":" << th_goodies->th_enum_
+<< " mono: " << th_goodies->monolithic_
+ << " thread name: " << th_goodies->thread_name_->c_string()->c_str()
+ << " FQN: " << th_goodies->fully_qualified_th_name_.c_str()
+ << " K: " << th_goodies->fsm_comments_->c_string()->c_str()
+ << std::endl;
+ }
+
+@*2 Count and re-align threads enumerate values to sorted position.\fbreak
+The |NO_OF_THREADS| is calculated at the same time. It is used to indicate thread presence
+and the numbers of threads to emit.
+@<count and re-align threads enumerate values to sorted position@>=
+ std::vector<table_entry*>::iterator ri = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator rie = GRAMMAR_DICTIONARY.end();@/
+ for(int p = -1;ri != rie;++ri){
+ ++p;
+ table_entry* tbl_entry = *ri;
+ @=thread_attributes*th_goodies= ((th_in_stbl*)tbl_entry->symbol_)->thread_in_stbl();@>@/
+ th_goodies->th_enum_ = p;
+ if(th_goodies->monolithic_ == 'n') ++NO_OF_THREADS;
+ }
+
+@ Check whether Linker has enough space to generate the thread bit maps.\fbreak
+See |Passover on code| for my comments.
+@<check whether Linker has enough space to gen thread bit maps: no throw up@>=
+int max_thds_supported = SPECULATIVE_NO_BIT_WORDS * BITS_PER_WORD;
+if(NO_OF_THREADS > max_thds_supported ){
+ char a[SMALL_BUFFER_4K];
+@.Error: not enough space for thread bit map manufacture!@>;
+ KCHARP msg =
+ "Error: not enough space for thread bit map manufacture!"
+ " # threads: %i, Linker's maximum no of threads supported: %i. \n"
+ " Please expand SPECULATIVE_NO_BIT_WORDS";
+ sprintf(a,msg,NO_OF_THREADS,max_thds_supported);
+ Yacco2_faulty_precondition(msg,__FILE__,__LINE__);
+ exit(1);
+}
+
+@** Thread graphs: first set generation.
+@*2 |Visit_graph|.
+@<External rtns and ...@>+=
+extern char Visit_graph[RESERVE_FIXED_NO_THREADS];
+@*3 Probagate ${\vert+\vert}$.\fbreak
+Not much to it. The ``all shift'' operator indicates all terminals.
+It is a wild token facility that eases the pain in using grammars.
+So, all terminals except some meta operators must be placed into
+the first set and the thread id against each terminal.
+
+To lower the \olinker document, the \ALLshift
+meta terminal representing all terminals substitutes ``eolr''
+in the thread's first set.
+It certainly makes for a cleaner document without the slug fest.
+@<probagate ${\vert+\vert}$@>=
+ INT_SET_ITER_type t_listi = Root_thread.fs_.find(LR1_EOLR);// substitute eolr
+ if(t_listi == Root_thread.fs_.end()){
+ Root_thread.fs_.insert(LR1_EOLR);
+ }
+
+ int no_of_T = T_DICTIONARY.size();// rel 1 instead of 0
+ for(int x=0;x<no_of_T;++x){
+ switch(x){
+ case LR1_QUESTIONABLE_SHIFT_OPERATOR:break;
+ case LR1_EOG:break;
+ case LR1_EOLR:continue;
+ case LR1_PARALLEL_OPERATOR:continue;
+ case LR1_REDUCE_OPERATOR:continue;
+ case LR1_INVISIBLE_SHIFT_OPERATOR:continue;
+ case LR1_ALL_SHIFT_OPERATOR:continue;
+ case LR1_FSET_TRANSIENCE_OPERATOR:continue;
+ default:break;
+ }
+ if(Visited_th.monolithic_ == 'n'){
+ INT_SET_type& th_list = T_THREAD_ID_LIST[x];
+ if(th_list.find(Visited_th.th_enum_) ==th_list.end()){
+ th_list.insert(Visited_th.th_enum_);
+ }
+ if(th_list.find(Root_thread_id) ==th_list.end()){
+ th_list.insert(Root_thread_id);
+ }
+ }
+ }
+
+@*3 Deal with threads having T in first set.\fbreak
+Please note that threads only are dealt with and not their standalone brethren.
+The root grammar is still traversed so that
+its called threads can be added to the list.
+
+Though the monolithic grammar is not launched by its first set,
+I included its first set calculations to verify my work.
+@<deal with threads having T in first set@>=
+ INT_SET_ITER_type t_listi = Root_thread.fs_.find(t_enum);
+ if(t_listi == Root_thread.fs_.end()){
+ Root_thread.fs_.insert(t_enum);
+ }
+if(Visited_th.monolithic_ == 'n'){
+ INT_SET_type& th_list = T_THREAD_ID_LIST[t_enum];@/
+ if(th_list.find(Visited_th.th_enum_) ==th_list.end()){
+ th_list.insert(Visited_th.th_enum_);
+ }
+ if(th_list.find(Root_thread_id) ==th_list.end()){
+ th_list.insert(Root_thread_id);
+ }
+}
+
+@*3 Associate native terminals with called thread.\fbreak
+For the moment until i can refine the thread's first set algorithm
+in \o2 that generates the Tes for ``list-of-native-first-set-terminals'',
+ i force ``attempting to run'' the threads
+having the \TRAshift in their first set across all Tes.
+There will be thread stutters in firing them up
+and then shuting them down when their true
+first set is outside of the current token.
+Remember these threads are fired up when they are in the
+being run grammar's current lr parse state.
+So the speed bump shouldn't be too big.
+
+@<associate native terminals with called thread@>=
+ std::vector<int>::iterator fi = Visited_th.list_of_Ts_.begin();@/
+ std::vector<int>::iterator fie = Visited_th.list_of_Ts_.end();@/
+ for(;fi!=fie;++fi){
+ int t_enum = *fi;
+ if(t_enum == LR1_ALL_SHIFT_OPERATOR){
+ @<probagate ${\vert+\vert}$@>;
+ }
+ @<deal with threads having T in first set@>;
+ }
+
+@*3 Process called thread's list.\fbreak
+Walk the list and recursively call that graph.
+@<process called thread's list@>=
+std::vector<thread_attributes*>::iterator li = Visited_th.list_of_transitive_threads_.begin();
+std::vector<thread_attributes*>::iterator lie = Visited_th.list_of_transitive_threads_.end();
+for(;li!=lie;++li){
+ thread_attributes* th_att = *li;
+lrclog << "------->process called thread's list thd: "
+<< th_att->thread_name_->c_string()->c_str()
+<< " for root thd id: " << Root_thread_id
+<< endl;
+ crt_fset_of_thread(*th_att,Root_thread_id,Root_thread);
+}
+
+@*2 |crt_fset_of_thread|.\fbreak
+Recursive procedure that chews gum, pats its stomach, and belches fire.\fbreak
+It traverses the called threads recursively to continue
+the association of the inherited terminals into their thread bit maps.
+Uses the |Visit_graph| to prevent self looping: whichever
+way u call it left or right recursion depending on your context ---
+cheers or bottoms-up.
+
+The |Root_thread_id| associates the starting thread id
+to the traversed called threads' first sets' terminals.
+Each thread is processed individually to associate itself
+with their called brethern's first sets.
+
+@<accrue linker code@>+=
+void crt_fset_of_thread
+ (thread_attributes& Visited_th
+ ,int Root_thread_id,thread_attributes& Root_thread){
+ if(Visit_graph[Visited_th.th_enum_] == 'y') return;
+ Visit_graph[Visited_th.th_enum_] = 'y';
+ @<associate native terminals with called thread@>;
+ @<process called thread's list@>;
+}
+
+@*2 Allocation space for |Visit_graph|.\fbreak
+Before, reserve allocated its space, now MS throws an error
+as |push_back| not done.
+@<allocation space for |Visit_graph|@>=
+ for(int vi = 0;vi<NO_OF_THREADS;++vi){
+ Visit_graph[vi] = 'n';
+ }
+
+@*2 Initialize |Visit_graph| to ``not visited''.\fbreak
+Each new thread having its final fist set's gened presets the graph.
+@<initialize |Visit_graph| to not visited@>=
+for(int vi = 0;vi<NO_OF_THREADS;++vi){
+ Visit_graph[vi] = 'n';
+}
+
+@*2 Generate those first sets.\fbreak
+Walk the what? u threads fulfull your first set destinies.
+Not very complex.
+A visit graph
+is built having its nodes equal in number to the threads
+in the |GRAMMAR_DICTIONARY|.
+Each node is initialized to ``not visited''.
+From here, it's just process each thread and visit its called threads to
+inherit their native terminals: Of course this is a transitive process.
+Sorry for the let down but there ain't much to it.
+
+A secondary graph of called threads per grammar is built so that
+ an overall linker document can be emitted with:\fbreak
+\ptindent{1) index of threads sorted by their name with its fsm's comments}
+\ptindent{2) followed by the monolithic grammars}
+\ptindent{3)) each grammar will have its called threads}
+@<generate threads final first sets@>=
+@<allocation space for |Visit_graph|@>;
+
+ std::vector<table_entry*>::iterator thi = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator thie = GRAMMAR_DICTIONARY.end();@/
+ for(;thi != thie;++thi){// individually process each thread
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ if(th_att->monolithic_ == 'n'){
+yacco2::lrclog << "thread being walked: "
+ <<th_att->thread_name_->c_string()->c_str() << " id: "
+<< th_att->th_enum_ << std::endl;
+ @<initialize |Visit_graph| to not visited@>;
+ crt_fset_of_thread(*th_att,th_att->th_enum_,*th_att);
+ }
+ }
+ thi = GRAMMAR_DICTIONARY.begin();@/
+ thie = GRAMMAR_DICTIONARY.end();@/
+ for(;thi != thie;++thi){// individually process each thread
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ @<initialize |Visit_graph| to not visited@>;
+ crt_called_thread_graph(*th_att);
+ }
+
+@** Generate document for each grammar's called threads.\fbreak
+@*3 |crt_called_thread_list| and |walk_called_thread_list|.\fbreak
+Generate the call graph per thread for reporting purposes.
+@<accrue linker code@>+=
+void walk_called_thread_list(std::vector<thread_attributes*>& Thd_list,AST* Mother_thd_t){
+ if(Thd_list.begin() == Thd_list.end()) return;
+ std::vector<thread_attributes*>::iterator li = Thd_list.begin();
+ std::vector<thread_attributes*>::iterator lie = Thd_list.end();
+ for(;li!=lie;++li){
+ thread_attributes* th_att = *li;
+ if(Visit_graph[th_att->th_enum_] == 'y') continue;
+ Visit_graph[th_att->th_enum_] = 'y';
+ AST* called_t = new AST(*th_att);
+ AST::add_child_at_end(*Mother_thd_t,*called_t);
+ walk_called_thread_list(th_att->list_of_transitive_threads_,called_t);
+ }
+}
+
+@*3 |crt_called_thread_graph|.
+@<accrue linker code@>+=
+void crt_called_thread_graph(thread_attributes& Visited_th){
+ if(Visit_graph[Visited_th.th_enum_] == 'y') return;
+ Visit_graph[Visited_th.th_enum_] = 'y';
+ AST* mother_thd_t = new AST(Visited_th);
+ Visited_th.called_thread_graph_ = mother_thd_t;
+ walk_called_thread_list(Visited_th.list_of_transitive_threads_,mother_thd_t);
+}
+@*3 |gen_each_thread_s_referenced_threads|.
+@<accrue linker code@>+=
+void gen_each_grammar_s_referenced_threads(){
+ std::vector<table_entry*>::iterator thi = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator thie = GRAMMAR_DICTIONARY.end();@/
+ for(;thi != thie;++thi){// individually process each thread
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ @<initialize |Visit_graph| to not visited@>;
+ crt_called_thread_graph(*th_att);
+ }
+}
+@*1 Generate Linker's document.\fbreak
+A secondary graph of called threads per grammar is built so that
+ an overall linker document can be emitted with:\fbreak
+\ptindent{1) index of threads sorted by their name with its fsm's comments}
+\ptindent{2) followed by the monolithic grammars}
+\ptindent{3)) each grammar will have its ``called threads'' graph}
+
+@*4 Make grammar's contents cweaveable and output.
+@<make grammar's contents cweaveable and output@>=
+ XLATE_SYMBOLS_FOR_cweave(th_att->thread_name_->c_string()->c_str(),xlate_gfile);
+ XLATE_SYMBOLS_FOR_cweave(th_att->fsm_comments_->c_string()->c_str(),rebuild_comment);
+ strcat(fandk,xlate_gfile);
+ strcat(fandk," --- ");
+ strcat(fandk,rebuild_comment);
+ int fandk_len = strlen(fandk);
+ if(fandk_len < CWEAVE_TITLE_LIMIT){
+ if(fandk[fandk_len-1] != '.'){
+ strcat(fandk,".");
+ }
+ }else{
+ if(fandk_len == CWEAVE_TITLE_LIMIT){
+ if(fandk[CWEAVE_TITLE_LIMIT-1] != '.'){
+ strcat(fandk,".");
+ }
+ }else{
+ fandk[CWEAVE_TITLE_LIMIT] = (char)0;
+ strcat(fandk,"$\\ldots$ .");
+ }
+ }
+ int x = sprintf(big_buf_
+ ,w_grammar
+ ,fandk
+ );
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+ x = sprintf(big_buf_
+ ,w_comments
+ ,rebuild_comment
+ );
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+ x = sprintf(big_buf_
+ ,w_called_threads," "
+ );
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+
+@*4 Output grammar's called threads list.
+@<output grammar's called threads list@>=
+ prt_called_thread_list_ast_functor prt_functr(&PRINT_CALLED_THREAD_LIST);
+ prt_functr.o_file(&ow_linker_file_);
+ ast_prefix pre(*th_att->called_thread_graph_,&prt_functr);
+ while (pre.base_stk_.cur_stk_rec() != 0){
+ pre.exec();
+ }
+
+@*4 Output grammar's used threads.
+@<output grammar's used threads@>=
+std::map<std::string, std::vector<std::string> >::iterator ti =
+ USED_THREADS_LIST.find(th_att->thread_name_->c_string()->c_str());
+ ow_linker_file_ << "{\\parindent=6pc" << endl;
+ ow_linker_file_ << "\\item{Used threads:}" << std::endl;
+ KCHARP used_threads =
+ "%s\n"
+ "@@.%s@@>";// xref entry
+ std::vector<std::string>& tt = ti->second;
+ std::vector<std::string>::iterator tti = tt.begin();
+ std::vector<std::string> ::iterator ttie= tt.end();
+ char xlate_thnm[Max_cweb_item_size];
+ for(;tti!=ttie;++tti){
+ XLATE_SYMBOLS_FOR_cweave(tti->c_str(),xlate_thnm);
+ int x = sprintf(big_buf_,used_threads,xlate_thnm,xlate_thnm);
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+ }
+ if(ti->second.empty() == YES) ow_linker_file_ << " none" << endl;
+ ow_linker_file_ << "}" << endl;
+
+@*4 Output grammar's first set.\fbreak
+Go thru the set and map the T enum into its literal value.
+@<output grammar's first set@>=
+ ow_linker_file_ << "{\\parindent=6pc" << endl;
+ ow_linker_file_ << "\\item{First set:}" << std::endl;
+ KCHARP fs =
+ "%s\n"
+ "@@.%s@@>";// xref entry
+ INT_SET_ITER_type fsi = th_att->fs_.begin();
+ INT_SET_ITER_type fsie = th_att->fs_.end();
+ char xlate_tnm[Max_cweb_item_size];
+ for(;fsi!=fsie;++fsi){
+ int tenum = *fsi;
+ table_entry* t_entry = T_DICTIONARY[tenum];
+ tth_in_stbl* t_in_stbl = (tth_in_stbl*)t_entry->symbol_;
+ T_attributes * t_att = t_in_stbl->t_in_stbl();
+ XLATE_SYMBOLS_FOR_cweave(t_att->fully_qualified_T_name_.c_str(),xlate_tnm);
+ int x = sprintf(big_buf_,fs,xlate_tnm,xlate_tnm);
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+ }
+
+@*3 Output preamble of document.
+@<output preamble of document@>=
+ KCHARP w_doc_index =
+ "\\input \"supp-pdf\"\n"
+ "\\input \"/usr/local/yacco2/diagrams/o2mac.tex\"\n"
+ "\\IDXlinkerdoctitle{%s}{%s}{%s}";
+ char xlate_file[Max_cweb_item_size];xlate_file[0] = (char)0;
+ char xlate_fscfile[Max_cweb_item_size];xlate_fscfile[0] = (char)0;
+ XLATE_SYMBOLS_FOR_cweave(w_linker_filename_.c_str(),xlate_file);
+ XLATE_SYMBOLS_FOR_cweave(cntl_file_name.c_str(),xlate_fscfile);
+ int x = sprintf(big_buf_
+ ,w_doc_index
+ ,xlate_file
+ ,xlate_file
+ ,xlate_fscfile
+ );
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+ KCHARP w_doc_comments =
+ "@@** O2linker Index of Grammars.\\fbreak\n"
+ "The grammars are sorted lexicographically into 2 parts:\n"
+ "threads followed by the stand alone grammars.\n"
+ "Each grammar's called threads graph is determined from their \n"
+ " ``list-of-transitive-threads''\n"
+ "derived from this construct.%s";
+ x = sprintf(big_buf_,w_doc_comments," ");
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+
+@*3 Loop thru grammars to gen their local linker doc info.
+@<loop thru grammars to gen their local linker doc info@>=
+ KCHARP w_grammar =
+ "@@*2 %s";
+ KCHARP w_comments =
+ "\\Linkeridxentryk{%s}";
+ KCHARP w_called_threads =
+ "\\Linkercalledthreadstitle%s";
+ char xlate_gfile[Max_cweb_item_size];
+ char rebuild_comment[Max_cweb_item_size];
+ char fandk[Max_cweb_item_size];
+ char xlate_thnm[Max_cweb_item_size];
+ char xlate_tnm[Max_cweb_item_size];
+
+ std::vector<table_entry*>::iterator ithi = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator ithie = GRAMMAR_DICTIONARY.end();@/
+ for(;ithi != ithie;++ithi){// individually process each thread
+ xlate_gfile[0] = (char)0;
+ rebuild_comment[0] = (char)0;
+ fandk[0] = (char)0;
+ xlate_thnm[0] = (char)0;
+ xlate_tnm[0] = (char)0;
+
+ table_entry* tbl_entry = *ithi;
+ @=thread_attributes*th_att= ((th_in_stbl*)tbl_entry->symbol_)->thread_in_stbl();@>@/
+ @<make grammar's contents cweaveable and output@>;
+ @<output grammar's called threads list@>;
+ @<output grammar's first set@>;
+ @<output grammar's used threads@>;
+ }
+
+@*3 Output First set of linker.
+@<output First set of linker@>=
+ KCHARP w_fsc_file_listing =
+ "@@** First set control file (fsc) listing.\\fbreak\n"
+ "File : ``%s''\\fbreak\n"
+ "\\let\\setuplistinghook = \\relax\n"
+ "\\listing{\"%s\"}\n";
+ char xlated_filename[Max_cweb_item_size];
+
+XLATE_SYMBOLS_FOR_cweave(cntl_file_name.c_str(),xlated_filename);
+ x = sprintf(big_buf_
+ ,w_fsc_file_listing
+ ,xlated_filename
+ ,cntl_file_name.c_str()
+ );
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+
+@*3 Output Index of linker.
+@<output Index of linker@>=
+ KCHARP w_index =
+ "@@** Index.%s";
+ x = sprintf(big_buf_,w_index," ");
+ ow_linker_file_.write(big_buf_,x);
+ ow_linker_file_ << std::endl;
+
+
+@*2 Output driver of the linker document.
+@<generate linker document@>=
+ gen_each_grammar_s_referenced_threads();
+ char big_buf_[BIG_BUFFER_32K];
+ std::string w_linker_filename_("o2linker_doc.w");
+ std::ofstream ow_linker_file_;
+ ow_linker_file_.open(w_linker_filename_.c_str(),ios_base::out|ios::trunc);
+ @<output preamble of document@>;
+ @<loop thru grammars to gen their local linker doc info@>;
+ @<output First set of linker@>;
+ @<output Index of linker@>;
+ ow_linker_file_.close();
+
+@** Emit code.\fbreak
+There is not too much to emit.\fbreak
+\ptindent{1) cpp preamble code --- time of day, and the grammar writer's preamble}
+\ptindent{2) threads include files and their namespace statement}
+\ptindent{3) global bit maps}
+\ptindent{4) global thread stable}
+\ptindent{5) global terminals and their thread bit maps}
+
+As an afterthought, the situation of having no threads to emit has been added.
+@<emit code@>=
+ cout << "Emit file name: " << linker_cntl_file_fsm.emitfile_filename_.c_str() << endl;
+ ofstream ofile(linker_cntl_file_fsm.emitfile_filename_.c_str(),ios::out);
+ if (!ofile){
+ cout << "Error - can't open emit file: "
+ << linker_cntl_file_fsm.emitfile_filename_.c_str() << endl;
+ return 1;
+ }
+ emit_cpp_preamble(ofile
+ ,linker_cntl_file_fsm.emitfile_filename_.c_str()
+ ,linker_cntl_file_fsm.preamble_srce_->syntax_code()->c_str());
+ emit_global_thread_include_files(ofile);
+ emit_global_bit_maps(ofile);
+ if(NO_OF_THREADS == 0){
+ emit_no_threads(ofile);
+ }else{
+ emit_global_thread_stable(ofile);
+ emit_T_fs_of_potential_threads(ofile);
+ }
+ ofile.close();
+
+@*2 Emit no threads.\fbreak
+A situation where the grammar writer has only standalone grammars; there
+are no parallel statements used within the grammars:
+${\vert\vert\vert}$ "returned token" called "thread".
+@<accrue linker code@>+=
+void emit_no_threads(ofstream& ofile){
+ ofile << "// There are NO THREADS emitted" << endl;
+ ofile << "void* yacco2::THDS_STABLE__ = 0;" << endl;
+ ofile << "void* yacco2::T_ARRAY_HAVING_THD_IDS__ = 0;" << endl;
+}
+
+@*2 Emit cpp preamble.
+@<accrue linker code@>+=
+void emit_cpp_preamble(ofstream& ofile,const char* OFile,const char* Preamble){
+ ofile << "//" << endl;
+ ofile << "// File: " << OFile << endl;
+ ofile << "// Generated by linker.exe" << endl;
+ ofile << "// Date and Time: " << DATE_AND_TIME() << endl;
+ ofile << "//" << endl;
+ ofile << endl;
+ ofile << "// Preamble code" << endl;
+ ofile << Preamble << endl;
+}
+
+@*2 Emit thread include files.\fbreak
+Unfortunately, a lot of verbage to resolve the thread's
+procedure. OhHum.
+
+Note, standalone grammars are not emitted. Why process them anyway?
+They provide thread calls that are added to the terminal's thread bit map.
+ @<accrue linker code@>+=
+void emit_global_thread_include_files(ofstream& ofile){
+ ofile << "// thread include and namespace" << std::endl;
+ char a[SMALL_BUFFER_4K];
+ KCHARP thread_include_ns = "#include \"%s.h\"";
+
+ std::vector<table_entry*>::iterator thi = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator thie = GRAMMAR_DICTIONARY.end();@/
+ for(;thi != thie;++thi){
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ if(th_att->monolithic_ == 'y') break;
+ int x = sprintf
+ (a
+ ,thread_include_ns
+ ,th_att->grammar_file_name_->c_string()->c_str()
+ );
+ ofile.write(a,x);
+ ofile << std::endl;
+ }
+}
+
+@*2 Emit global bit maps.
+@<accrue linker code@>+=
+void emit_global_bit_maps(ofstream& ofile){
+ ofile << "// BIT MAPS" << std::endl;
+ ofile << "#define TOTAL_NO_BIT_WORDS 2*1024*50" << std::endl;
+ ofile << "int yacco2::TOTAL_NO_BIT_WORDS__(TOTAL_NO_BIT_WORDS);" << std::endl;
+ ofile << "yacco2::ULINT bit_maps[TOTAL_NO_BIT_WORDS];" << std::endl;
+ ofile << "void* yacco2::BIT_MAPS_FOR_SALE__ = (void*)&bit_maps;" << std::endl;
+ ofile << "int yacco2::BIT_MAP_IDX__(0);" << std::endl;
+}
+
+@*2 Emit global thread stable |THDS_STABLE__|.\fbreak
+Read the |GRAMMAR_DICTIONARY| and emit a sorted |Thread_entry| list where
+each thread (excluded are the standalone grammars)
+has a global naming convention of Ixxx where the xxx is the thread name.
+The |Thread_entry| provides its literate name,
+ the thread function to be spawned, and its enumerate value
+used as an index into the array of threads.
+
+|THDS_STABLE__| is a global referenced by Yacco2's runtime library.
+It is a structure indicating the number of threads within its array and
+the array of addresses to each just-gened thread's |Thread_entry|. HoHum --- is
+this better than fe-fi-foe-fum I smell the blood of an ...?
+@<accrue linker code@>+=
+void emit_global_thread_stable(ofstream& ofile){
+ ofile << "// THREAD STABLE" << std::endl;
+ char a[BIG_BUFFER_32K];
+ KCHARP thread_entry = "yacco2::Thread_entry I%s = {%s,%s,%i,%s::PROC_%s};";
+ string quoted_name;
+ @<gen thread list@>;
+ @<gen global thread array@>;
+}
+
+@*2 The threading stew.
+@<gen thread list@>=
+ std::vector<table_entry*>::iterator thi = GRAMMAR_DICTIONARY.begin();@/
+ std::vector<table_entry*>::iterator thie = GRAMMAR_DICTIONARY.end();@/
+ for(;thi != thie;++thi){
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ if(th_att->monolithic_ == 'y') break;
+ quoted_name.clear();
+ const char* th_name = th_att->thread_name_->c_string()->c_str();
+ quoted_name += '"';
+ quoted_name += th_name;
+ quoted_name += '"';
+
+ int x = sprintf(a,thread_entry,th_name,quoted_name.c_str()
+ ,th_att->fully_qualified_th_name_.c_str(),th_att->th_enum_
+ ,th_att->name_space_name_->c_string()->c_str()
+ ,th_att->thread_name_->c_string()->c_str());
+ ofile.write(a,x);
+ ofile << std::endl;
+ }
+
+@*2 The table h\^ote.\fbreak
+The thoroughbreds waiting for the ``and they'rrrre off''.
+@<gen global thread array@>=
+ div_t c = div(NO_OF_THREADS,BITS_PER_WORD);
+ if(c.rem != 0) ++c.quot;
+ NO_WORDS_FOR_BIT_MAP = c.quot;
+
+ KCHARP thread_array =
+ "struct thd_array_type {\n"
+ " yacco2::USINT no_entries__;\n"
+ " yacco2::Thread_entry* first_entry__[%i];"
+ "};\n"
+ "thd_array_type thd_array = {\n"
+ " %i\n"
+ " ,\n"
+ " {\n";
+ int x = sprintf(a,thread_array,NO_OF_THREADS,NO_OF_THREADS);
+
+ ofile.write(a,x);
+ ofile << std::endl;
+ bool first_entry(true);
+ thi = GRAMMAR_DICTIONARY.begin();
+ thie = GRAMMAR_DICTIONARY.end();
+ KCHARP thread_entry_name = "&I%s";
+ for(;thi!=thie;++thi){
+ @=th_in_stbl*th_tbl= (th_in_stbl*)(*thi)->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ if(th_att->monolithic_ == 'y') break;
+ if(first_entry == true){
+ first_entry = false;
+ ofile << " ";
+ }else{
+ ofile << " ,";
+ }
+ int x = sprintf(a,thread_entry_name,th_att->thread_name_->c_string()->c_str());
+ ofile.write(a,x);
+ ofile << std::endl;
+ }
+ ofile << " }\n};" << endl;
+ @<announce the stable to the world@>;
+
+@*2 Announce the stable to the world.
+@<announce the stable to the world@>=
+ ofile << "void* yacco2::THDS_STABLE__ = (void*)&thd_array;" << endl;
+
+@*2 Emit global Terminals' thread bit maps.\fbreak
+This is the inverse to first sets: these are the threads that can
+run from the specific terminal.
+
+This global optimization determines whether the finite state table
+has the potential to run a thread. How so?
+Firstly, the local grammar determines
+whether threading is taking place in its current state configuration.
+If so, the current token is
+checked to see whether there are threads to possibly run using
+the global thread bit map specific to itself.
+With these potental threads the local state
+configuration is measured for activity.
+Then and only then will the
+just-in-time dynamics of building the grammar's local thread map occur and the
+found threads launched.
+
+This optimization stops stuttering: how so?
+Only threads having the current token in their first set get launched.
+The jiggles now are only real potential prefixes to parse by each launched thread.
+Remember, common prefixes get resolved by arbitration within the
+launching grammar specific to the current finite state configuration.
+
+Why the output to another file? The flatulence of Microsoft's compiler: an
+INTERNAL COMPILER ERROR ``C1001'' message.
+Well I found the typo that causes this draconian behavior: ``endl::endl''
+instead of ``std::endl''. This congers up speculative thoughts on how
+Microsoft's compiler is written. Enough of my racket: Back to appending
+to the same file.
+
+@<accrue linker code@>+=
+void emit_T_fs_of_potential_threads(ofstream& ofile){
+ ofile << "// Terminal thread sets" << std::endl;
+ int no_of_T = T_DICTIONARY.size();
+ char a[SMALL_BUFFER_4K];
+ KCHARP T_list_to_thd_list_type =
+ "struct T_%i_type{\n"
+ " yacco2::ULINT first_entry__[%i];\n"
+ "};\n";@/
+ KCHARP T_list_to_thd_list_var = "T_%i_type T_%i = {// for T: %s";@/
+ KCHARP thd_id_in_list = "//%i: %s";@/
+
+ INT_SET_LIST_ITER_type i = T_THREAD_ID_LIST.begin();@>@/
+ INT_SET_LIST_ITER_type ie = T_THREAD_ID_LIST.end();@>@/
+ int terminal_id(-1);
+ for(;i!=ie;++i){
+ ++terminal_id;@/
+ INT_SET_type& th_list = *i;
+ if(th_list.empty() == true) continue;
+ int x = sprintf(a,T_list_to_thd_list_type,terminal_id,NO_WORDS_FOR_BIT_MAP);
+ ofile.write(a,x);
+ ofile << std::endl;
+ @<create terminal's thread bit map@>;
+ }
+ @<emit array of Terminals' thread bit maps@>;
+}
+
+
+@*2 Create terminal's thread bit map.\fbreak
+As the number of threads are unknown,
+I use |SPECULATIVE_NO_BIT_WORDS| to reserve the space to manufacture
+the thread maps.
+See my comments in |Passover on code|.
+@<create terminal's thread bit map@>=
+ int no_thds_ids = th_list.size();
+ table_entry* t_entry = T_DICTIONARY[terminal_id];
+ tth_in_stbl* t_in_stbl= (tth_in_stbl*)t_entry->symbol_;
+ T_attributes* t_att= t_in_stbl->t_in_stbl();
+ x = sprintf(a
+ ,T_list_to_thd_list_var
+ ,terminal_id
+ ,terminal_id
+ ,t_att->fully_qualified_T_name_.c_str()
+ );
+ ofile.write(a,x);
+ ofile << std::endl;@/
+ ULINT word_map[SPECULATIVE_NO_BIT_WORDS]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ INT_SET_ITER_type j = th_list.begin();
+ INT_SET_ITER_type je = th_list.end();
+
+ @<calculate terminal's thread bit map@>;
+ @<emit terminal's thread bit map@>;
+
+@*2 Calculate terminal's thread bit map.
+And print out their contents as comments.
+@<calculate terminal's thread bit map@>=
+ for(;j!=je;++j){
+ int th_id = *j;
+ table_entry* tbl_entry = GRAMMAR_DICTIONARY[th_id];
+ @=th_in_stbl*th_tbl= (th_in_stbl*)tbl_entry->symbol_;@> @/
+ thread_attributes* th_att = th_tbl->thread_in_stbl();
+ div_t bb = div(th_id,BITS_PER_WORD);
+ ULINT bit_pos_value = 1 << bb.rem;
+ word_map[bb.quot] |= bit_pos_value;
+ int x = sprintf(a,thd_id_in_list,th_id,th_att->thread_name_->c_string()->c_str());
+ ofile.write(a,x);
+ ofile << std::endl;
+ }
+
+@*2 Emit terminal's thread bit map.
+@<emit terminal's thread bit map@>=
+ for(int dd=1;dd<=NO_WORDS_FOR_BIT_MAP;++dd){
+ if(dd == 1) ofile << " {";
+ else ofile << " ,";
+ ofile << word_map[dd-1]<< endl;
+ }
+ ofile << " }" << endl;
+ ofile << "};" << endl;
+
+ @*2 Emit Terminals' thread bit maps and global |T_ARRAY_HAVING_THD_IDS__|.\fbreak
+ Go tell it to the ? or is it Yacco2?
+ @<emit array of Terminals' thread bit maps@>=
+ KCHARP T_array_type =
+ "struct t_array_type {\n"
+ " yacco2::USINT no_entries__;\n"
+ " yacco2::thd_ids_having_T* first_entry__[%i];\n"
+ "};";
+ int x = sprintf(a,T_array_type,no_of_T);
+ ofile.write(a,x);
+ ofile << std::endl;
+
+ KCHARP T_array =
+ "t_array_type t_array = {\n"
+ " %i\n"
+ " ,{";
+ x = sprintf
+ (a
+ ,T_array
+ ,no_of_T
+ );
+ ofile.write(a,x);
+ ofile << std::endl;
+
+ i = T_THREAD_ID_LIST.begin();
+ ie = T_THREAD_ID_LIST.end();@>@/
+ @<print out each thread set@>;
+ ofile << " }\n};" << endl;
+ ofile << "void* yacco2::T_ARRAY_HAVING_THD_IDS__ = (void*)&t_array;" << endl;
+
+@*2 Print each entry.\fbreak
+More coughing. Oh well.
+@<print out each thread set@>=
+ bool first_item(true);// regulates if a comma should be emitted
+ for(int t_id = -1;i!=ie;++i){
+ ++t_id;
+ table_entry* t_entry = T_DICTIONARY[t_id];@/
+ tth_in_stbl* t_in_stbl= (tth_in_stbl*)t_entry->symbol_;
+ T_attributes* t_att= t_in_stbl->t_in_stbl();
+ INT_SET_type& th_list = *i;
+ if(th_list.empty() == true){// no thds with this T as first set
+ if(first_item == true) first_item = false;
+ else ofile << " ,";
+ KCHARP T_array_entries = "%s// %s";
+ int x = sprintf(a,T_array_entries,"0",t_att->fully_qualified_T_name_.c_str());
+ ofile.write(a,x);
+ ofile << std::endl;
+ continue;
+ }else{
+ if(first_item == true) first_item = false;
+ else ofile << " ,";
+ KCHARP T_list_to_thd_list_var = "(yacco2::thd_ids_having_T*)&T_%i // %s";
+
+ int x = sprintf(a,T_list_to_thd_list_var,t_id,t_att->fully_qualified_T_name_.c_str());
+ ofile.write(a,x);
+ ofile << std::endl;
+ }
+ }
+
+@** Main line of Linker.
+@<accrue linker code@>=
+YACCO2_define_trace_variables();
+yacco2::TOKEN_GAGGLE Error_queue;@/
+STBL_T_ITEMS_type STBL_T_ITEMS;
+std::vector<NS_yacco2_terminals::table_entry*> GRAMMAR_DICTIONARY;@/
+std::map<std::string, std::vector<std::string> > USED_THREADS_LIST;@/
+std::vector<NS_yacco2_terminals::table_entry*> T_DICTIONARY;@/
+INT_SET_LIST_type T_THREAD_ID_LIST;
+
+int NO_OF_THREADS(0);@/
+int NO_WORDS_FOR_BIT_MAP(0);
+char Visit_graph[RESERVE_FIXED_NO_THREADS];
+extern void XLATE_SYMBOLS_FOR_cweave(const char* Sym_to_xlate,char* Xlated_sym);
+extern void PRINT_CALLED_THREAD_LIST
+ (yacco2::AST* Node,std::ofstream* Ow_linker_file,int Recursion_level);
+string cntl_file_name;
+yacco2::CHAR PRT_SW('n');
+
+int main(int argc, char* argv[])
+{
+ cout << yacco2::O2linker_VERSION << std::endl;
+ using namespace yacco2;
+ using namespace std;
+ load_linkkws_into_tbl();
+ cout << "Get command line and parse it " << endl;
+ GET_CMD_LINE(argc,argv,Linker_holding_file,Error_queue);
+ @<if error queue not empty then deal with posted errors@>;
+ LINKER_PARSE_CMD_LINE(Linker_holding_file,cntl_file_name,Error_queue);
+ @<if error queue not empty then deal with posted errors@>;
+ lrclog << yacco2::O2linker_VERSION << std::endl;
+ @<parse linker control file@>;
+ @<parse T alphabet@>;
+ @<parse fsc files@>;
+ @<post verify that there are no threads ``used'' and not ``defined''@>;
+ @<sort thread dictionary@>;
+ @<dump sorted dictionary@>;
+ @<count and re-align threads enumerate values to sorted position@>;
+ @<check whether Linker has enough space to gen thread bit maps: no throw up@>;
+ @<generate threads final first sets@>;
+ @<emit code@>;
+ @<generate linker document@>;
+ //|yacco2::Parallel_threads_shutdown(linker_cntl_file);|
+return 0;
+}