diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /web/yacco2/o2linker/prog.w |
Initial commit
Diffstat (limited to 'web/yacco2/o2linker/prog.w')
-rw-r--r-- | web/yacco2/o2linker/prog.w | 1134 |
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; +} |