@q file: parse_env.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% */@> @** Parse stack environment.\fbreak \fbreak \fbreak \convertMPtoPDF{"/usr/local/yacco2/diagrams/parse_stk_env.1"}{1}{1} \fbreak \fbreak Some general comments on the parse stack environment:\fbreak Firstly it's just an array of |parse_record| whereby the determinist push-down automaton straddles 2 array records: the first record contains the state address and its stacked symbol and the second record contains the goto state that it vectors to. To improve parsing speed, the rule's ``birth-run-delete'' cyle has been replaced by recycling of the rule: ``birth once run forever'' until the parser is shutdown. To do this a |Rule_s_reuse_entry| is kept per required number of recurse / use count per rule. This is determined by analysing the grammar and counting the rhs of each rule for the rule's use patterns. See |structure.w| of \O2{}library explaining this. Each grammar locally contains its ``rules's reuse'' table. The |reduce_rhs_of_rule| procedure reads the recyled rules's table and returns the dupple containing the rule and the address within the recycle table containing the |Rule_s_reuse_entry|. Both components are pushed onto the parse stack frame. When the parse stack frame is popped due to a reduce of the rhs of a rule or due to an abort, each stack frame being popped is inspected for its symbol context: Rule or Terminal, or possibly nothing. If the symbol context is of Rule, the |Rule_s_reuse_entry|'s ``in use'' indicator is reset for recycling. \fbreak \fbreak Another subtlety is that of ``how to reset the rule's object''?.\fbreak In c++ terms, as the rule's class only has ctor and possibly a dtor that are implicitly called by the generated code, ``how do u reset the object for reuse as this is not a copy situation?''. Not to blame c++, this situation was not thought of until now by me. This requires an inspection of the grammar rule's definition for a grammar's ``constructor'' directive that usually does specific initializations at time of rule creation. If it does not exist, then there is nothing to be done unless the grammar writer has defaulted to the compiler's initialization code for the class's locally defined variables --- as they say in French d\'esol\'e. For me this unspoken initialization is not good as it is implicit and i prefer being forthright to my coding intentions. Given this, a ``reuse type'' ctor must be defined within the rule's class containing the constructor directive's code if required and called inside the preliminaries of |reduce_rhs_of_rule| procedure for the specific rule. @*2 Parse record.\fbreak |Cparse_record| defines the record of the parse stack. Due to my way of |cweb| source code ordering, type definitions come before structure definitions. In this case,the structure definition is outputted as a type definition instead of as a structure.\fbreak \fbreak ``abort\_\_'' adjusted to ``void*'' from ``bool'' as my optimization on stack frame of individual structures being multiples got slack bytes generated when porting to a HP Aplha. So make sure all are of same size. Put back to bool. @+= struct Cparse_record{ void set_aborted(bool X); bool aborted()const; yacco2::CAbs_lr1_sym* symbol(); void set_symbol(yacco2::CAbs_lr1_sym* Symbol); yacco2::State* state(); void set_state(yacco2::State* State_no); void set_rule_s_reuse_entry(yacco2::Rule_s_reuse_entry* Rule_s_reuse); yacco2::Rule_s_reuse_entry* rule_s_reuse_entry(); yacco2::CAbs_lr1_sym* symbol__; yacco2::State* state__; bool aborted__; yacco2::Rule_s_reuse_entry* rule_s_reuse_entry_ptr__; }; @*2 Lr parse stack structure.\fbreak Why the home grown stack --- SPEED. Templates are toooo slow with to many generalities. @+= struct lr_stk{ lr_stk(); void lr_stk_init(yacco2::State& S1); void push_state(yacco2::State& S1); void push_symbol(yacco2::CAbs_lr1_sym& Sym); bool empty(); void pop(); void clean_up(); Cparse_record* sf_by_sub(yacco2::UINT Sub); Cparse_record* sf_by_top(yacco2::UINT No); Cparse_record lr_stk__[C_MAX_LR_STK_ITEMS]; yacco2::UINT top_sub__; Cparse_record* top__; Cparse_record* first_sf__; State* first_state__; }; @*2 Parse stack implementation. @= lr_stk::lr_stk(){ top_sub__ = 1; first_sf__ = &lr_stk__[1]; top__ = first_sf__; first_state__ = 0; top__->state__ = 0; top__->symbol__ = 0; top__->aborted__ = 0; top__->rule_s_reuse_entry_ptr__ = 0; } void lr_stk::lr_stk_init(yacco2::State& S1){ top_sub__ = 1; first_sf__ = &lr_stk__[1]; top__ = first_sf__; first_state__ = &S1; top__->state__ = first_state__; top__->symbol__ = 0; top__->aborted__ = 0; top__->rule_s_reuse_entry_ptr__ = 0; } bool lr_stk::empty(){ if(top_sub__ < 1) return true; return false; } void lr_stk::push_symbol(yacco2::CAbs_lr1_sym& Sym){ top__->symbol__ = &Sym; } void lr_stk::pop(){ --top_sub__; --top__; } void lr_stk::clean_up(){ top_sub__ = 1; first_sf__ = &lr_stk__[1]; top__ = first_sf__; top__->symbol__ = 0; top__->aborted__ = 0; top__->state__ = first_state__; top__->rule_s_reuse_entry_ptr__ = 0; } @ |lr_stk::clean_up()|. Speed demon. @<|lr_stk::clean_up()|@>= top_sub__ = 1; top__ = first_sf__; top__->symbol__ = 0; top__->aborted__ = 0; top__->state__ = first_state__; top__->rule_s_reuse_entry_ptr__ = 0; @ |lr_stk::empty()|. Speed demon. @<|lr_stk::lr_stk::empty()|@>= if(top_sub__ < 1) return true; return false; @ |lr_stk::pop()|. Speed demon. @<|lr_stk::pop()|@>= --top_sub__; --top__; @*2 Parse stack implementation. @= Cparse_record* lr_stk::sf_by_sub(yacco2::UINT Sub){ if((Sub < 1) || (Sub > MAX_LR_STK_ITEMS)){ char a[BUFFER_SIZE]; @.Err lr\_stk - sf\_by\_sub invali...@> yacco2::KCHARP msg = "lr_stk - sf_by_sub invalid sub: %i not in range 1..%i"; sprintf(a,msg,Sub,MAX_LR_STK_ITEMS); Yacco2_faulty_precondition(a,__FILE__,__LINE__); exit(1); } return &lr_stk__[Sub]; } Cparse_record* lr_stk::sf_by_top(yacco2::UINT No){ int s = top_sub__ - No; if(s < 1){ @.Err lr\_stk - sf\_by\_top underf...@> char a[BUFFER_SIZE]; yacco2::KCHARP msg = "lr_stk - sf_by_top underflow top sub: %i, requested sub: %i < 1"; sprintf(a,msg,top_sub__,No); Yacco2_faulty_precondition(a,__FILE__,__LINE__); exit(1); } return &lr_stk__[s]; } @*2 Parse stack implementation. @= void lr_stk::push_state(yacco2::State& S1){ if(top_sub__ >= MAX_LR_STK_ITEMS){ char a[BUFFER_SIZE]; @.Err lr\_stk - push overflow stac...@> yacco2::KCHARP msg = "lr_stk - push overflow stack max: %i"; sprintf(a,msg,MAX_LR_STK_ITEMS); Yacco2_faulty_precondition(a,__FILE__,__LINE__); exit(1); } ++top__; ++top_sub__; top__->state__ = &S1; top__->symbol__ = 0; top__->aborted__ = 0; top__->rule_s_reuse_entry_ptr__ = 0; } @ |lr_stk::push_state| --- Speed demon. @<|lr_stk::push_state|@>= if(parse_stack__.top_sub__ >= MAX_LR_STK_ITEMS){ char a[BUFFER_SIZE]; @.Err lr\_stk - push overflow stac...@> yacco2::KCHARP msg = "lr_stk - push overflow stack max: %i"; sprintf(a,msg,MAX_LR_STK_ITEMS); Yacco2_faulty_precondition(a,__FILE__,__LINE__); exit(1); } ++parse_stack__.top__; ++parse_stack__.top_sub__; parse_stack__.top__->state__ = Goto_state; parse_stack__.top__->symbol__ = 0; parse_stack__.top__->aborted__ = 0; parse_stack__.top__->rule_s_reuse_entry_ptr__ = 0; @*2 |set_aborted| and |aborted| implementation.\fbreak The |set_aborted| tags the parse stack record. It is used in conjunction with the symbol's |affected_by_abort| attribute. That is, the parallel parse aborted and it is cleaning up the partial effects of the parse: the symbol indirectly dictates the what's to be done. @= void yacco2:: Cparse_record:: set_aborted(bool X){ aborted__ = X; } bool yacco2:: Cparse_record:: aborted()const{ if(aborted__ == 0) return false; return true; } @*2 |set_rule_s_reuse_entr| and |rule_s_reuse_entry| implementation.\fbreak Used in the optimization of a rule's recycled symbol. It is the rule's subscript into the fsm's |rules_reuse_table|. @= void yacco2:: Cparse_record:: set_rule_s_reuse_entry(yacco2::Rule_s_reuse_entry* Rule_s_reuse){ rule_s_reuse_entry_ptr__ = Rule_s_reuse; } yacco2::Rule_s_reuse_entry* yacco2:: Cparse_record:: rule_s_reuse_entry(){ return rule_s_reuse_entry_ptr__; } @*2 |set_state| and |state| implementation. @= void yacco2:: Cparse_record:: set_state(yacco2::State* State_ptr){ state__ = State_ptr; } yacco2:: State* yacco2:: Cparse_record:: state(){ return state__; } @*2 |set_symbol| and |symbol| implementation. @= yacco2::CAbs_lr1_sym* yacco2:: Cparse_record:: symbol(){ return symbol__; } void yacco2:: Cparse_record:: set_symbol(yacco2::CAbs_lr1_sym* Symbol){ symbol__ = Symbol; }