@q file: tree.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% */@> @** Tree containers, functors, and walkers.\fbreak The |AST| structure allows one to build tree structures where each node enrobes a terminal symbol's address. Each node contains a left link representing dominance: parent to child relation, a right link representing equivalence: siblings or brothers --- your preference of terminology, and a previous link representing an older node; this can be nil as the node is the root, an older brother, or a parent as the node is the oldest child. The previous link depends on where within the tree the node sits. The left and right links can be nil indicating no children, or no younger brothers. To support the creation and walking of the trees, various static procedures are available. There are 2 tree walkers: prefix and postfix. The way the tree is built, there is no infix walker! The balance of the walkers are variants on these 2 that have restrictions on how much of the tree is to be read. Restriction 1: the node is a forest where pre and post fix walks are done --- though the node can be linked with brothers, as a forest it stays within its bounds. Restriction 2: breadth only walk --- walk self and younger brothers. Restriction 3: prefix with breadth only --- the node is considered a parent; walk itself and its immediate children. The container has 3 parts: the container of tokens that match the filtering mechanism, the parts needed to walk the tree, and a token access mechanism. As an optimization, the token access determines whether the requested token-by-number is in the container. This allows one to iterate randomly a tree structure. The tree walker linearizes the token stream. It uses a finite automaton with 5 elements in its alphabet: init, left, right, visit, eoc. These represent how the node has been processed. The left and right elements indicate that the dominance or equivalence link is being followed. The init, visit, and eoc are states on how the node was processed. Originally, the initial access of the node represented by `init', and the end of the node access before it is popped from the stack represented by `eoc' allowed the user to fine tune the walker's behavior but this was overkill. The `visit' state breaks out of the tree traversal and allows one to deal with the situation. Each tree walker implements these states in their `exec' and `advance' methods. To control the tree traversal, a stack is used due to the type of control needed to break out of the traversal. Recursion does not allow one to do this due to its implicit call stack and continuous behavior as opposed to discrete stepwise logic. The only difference to iterating the tree container versus the other token containers is a tree container can only be accessed by token-number. There is no STL type iterator. One accesses the container by its `operator[]' method iterating by the numbers started by 0. Ugh. To break out of the iteration, the returned terminal is tested against the |LR1_eog| terminal indicating end-of-tree met. A functor mechanism is available to capture info at time of the visited node. It can be a stand alone behaviour or it could be used in conjunction with a grammar. For example if a tree's node is being printed by use of a grammar, the recursion level count must be maintained by the functor and used by the grammar's subrule. Why not process the recursion count at the time of the grammar's subrule reduction? Remember: the lookahead terminal to reduce the subrule is the current stack configuration that is one ahead of what's needed. Hence the need for the functor and its registering of recursion level. As a tree structure is very large and diverse, to deal with specific node types, a set mechanism of inclusion or exclussion of symbols is supported. With these walkers and companions --- filters and functor, a tree is walked in linear fashion just like a normal token stream. This allows one to write grammars to consume tree structures in the same spirit as a to-be-parsed language. Typically these phases are the down stream stages of the semantic side to compilation. Really good stuff! @*2 Tree walker's traversal with filter mechanism. @= advance();// status advance int_set_iter_type i; CAbs_lr1_sym* sym; tree_traverse: { if(base_stk_.cur_stk_rec_ == 0) return; if(base_stk_.cur_stk_rec_->act_ != ast_base_stack::visit){ @; } sym = AST::content(*base_stk_.cur_stk_rec_->node_); if(base_stk_.filter_ == 0) @; filter_node:@/ @; @; reject_filter:@/ @; accept_filter:@/ @; next_t:@/ advance();// go fetch next node as current goto tree_traverse; } accept_t: @; return; @ Dispatch on filter type: accept or reject filter. @= if(base_stk_.accept_opt_ == true) goto accept_filter; else goto reject_filter; @ Go to next t. @= goto next_t; @ Go to accept t. @= goto accept_t; @ Fire off visit functor. @= yacco2::functor_result_type rr = base_stk_.action_->operator()(&base_stk_); switch (rr){ case yacco2::bypass_node: goto next_t; case yacco2::accept_node: return; case yacco2::stop_walking:{ base_stk_.cur_stk_rec_ = 0; return; } } @ Is node's content found in accept filter? no next t, yes accept t. @= if(i == base_stk_.filter_->end()) @; @; @ Is node's content found in bypass filter? yes next t, no accept t. @= if(i != base_stk_.filter_->end()) @; @; @ See if just read node's content is in filter set. @= i = base_stk_.filter_->find(sym->enumerated_id__); @*2 |ast_postfix| tree walker. @+= struct ast_postfix:public ast_stack{ ast_postfix(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Prefix tree walker. @+= struct ast_prefix:public ast_stack{ ast_prefix(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Postfix tree walker of self only.\fbreak The forest in its name indicates that it is considered a stand alone tree. It will not follow it's brother links. @+= struct ast_postfix_1forest:public ast_stack{ ast_postfix_1forest(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Prefix tree walker of a forest.\fbreak This only walks itself and its underlings. It does not follow its brother link. @+= struct ast_prefix_1forest:public ast_stack{ ast_prefix_1forest(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Breadth only tree walker.\fbreak Deal with self and younger siblings. @+= struct ast_breadth_only:public ast_stack{ ast_breadth_only(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Prefix with breadth only tree walker.\fbreak Parental walk with immediate children. @+= struct ast_prefix_wbreadth_only:public ast_stack{ ast_prefix_wbreadth_only(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void exec(); void advance(); }; @*2 Moon walking --- get ancestry for a specific node.\fbreak This walk goes up a tree looking for its ancestral goal node. It fills the list in youngest to oldest order where the last node being the goal node. The goal node allows u to stop partway thru the global tree: ie somewhere within a context. If no filter set is passed it defaults to all Tes accepted. The resultant list of ancestral nodes can be empty. If a functor is provided, it allow one to fine-tune the acceptance of an ancester or to recurse on its own tree walking: no inter-family feuds allowed?! @+= struct ast_moonwalk_looking_for_ancestors{ ast_moonwalk_looking_for_ancestors (AST& Moonchild,USINT Goal,Type_AST_ancestor_list& Ancestors,Type_AST_functor* Functor ,yacco2::int_set_type* Filter=0,bool Accept_opt=true); void let_s_moonwalk(); bool deal_with_parent(AST* Parent); functor_result_type let_s_functor(AST* Parent); bool deal_with_functor(AST* Parent); AST* moonchild_; USINT goal_; Type_AST_ancestor_list* ancestor_list_; Type_AST_functor* functor_; yacco2::int_set_type* filter_; bool filter_type_; bool filter_provided_; }; @*2 Tree implementations. @(wtree.cpp@>= @; @; @; @ Accrue tree code. @= // accrue tree code @* |ast_base_stack| implementation. @= yacco2::ast_base_stack:: ast_base_stack(Type_AST_functor* Action,yacco2::int_set_type* Filter,bool Accept_opt) :idx_(No_Token_start_pos) ,stk_(std::vector()) ,action_(Action) ,cur_stk_rec_(0) ,filter_(Filter) ,accept_opt_(Accept_opt){} yacco2::ast_base_stack:: ast_base_stack() :idx_(No_Token_start_pos) ,stk_(std::vector()) ,action_(0) ,cur_stk_rec_(0) ,filter_(0) ,accept_opt_(YES){} yacco2::ast_stack:: ast_stack(Type_AST_functor* Action,yacco2::int_set_type* Filter,bool Accept_opt) :base_stk_(Action,Filter,Accept_opt){} void yacco2::ast_base_stack:: pop(){ if(stk_.empty() == YES) return; --idx_; stk_.pop_back(); if (stk_.empty() == YES){ idx_ = No_Token_start_pos; cur_stk_rec_ = 0; return; } cur_stk_rec_ = &stk_[idx_]; } void yacco2::ast_base_stack:: push(AST& Node,ast_base_stack::n_action Action){ ++idx_; stk_.push_back(yacco2::ast_base_stack::s_rec()); cur_stk_rec_ = &stk_[idx_]; cur_stk_rec_->node_ = &Node; cur_stk_rec_->act_ = Action; } yacco2::INT yacco2::ast_base_stack:: cur_stk_index(){ return idx_; } yacco2::ast_base_stack::s_rec* yacco2::ast_base_stack:: cur_stk_rec(){ return cur_stk_rec_; } yacco2::ast_base_stack::s_rec* yacco2::ast_base_stack:: stk_rec(yacco2::INT I){ if(I > idx_) return 0; return &stk_[I]; } @* Tree walker implementations. @*2 |ast_postfix|.\fbreak This is your regular postfix tree walker of a complete tree. @+= yacco2::ast_postfix:: ast_postfix(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @*2 |ast_postfix| exec.\fbreak Originally this was a switch statement handling the 5 states. As this is a 80/20 situation, the if statement is more efficient: no need for the specifics. @+= void yacco2::ast_postfix:: exec(){ @; } @*2 |ast_postfix| advance. @+= void yacco2::ast_postfix:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { AST* down = AST::get_1st_son(*base_stk_.cur_stk_rec_->node_); if (down == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit;//bypass left return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::left; base_stk_.push(*down,ast_base_stack::init); return; } case ast_base_stack::left: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::visit: { AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc;//bypass return; } base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_prefix|.\fbreak Prefix walk of complete tree. @+= yacco2::ast_prefix:: ast_prefix(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @*2 |ast_prefix| exec. @+= void yacco2::ast_prefix:: exec(){ @; } @*2 |ast_prefix| advance. @+= void yacco2::ast_prefix:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::left: { AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::visit: { AST* lt = AST::get_1st_son(*base_stk_.cur_stk_rec_->node_); if(lt != 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::left; base_stk_.push(*lt,ast_base_stack::init); return; } AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if(rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_postfix_1forest|.\fbreak Forest postfix walk. Do not go outside its bounds. @+= yacco2::ast_postfix_1forest:: ast_postfix_1forest(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @ |ast_postfix_1forest| exec. @+= void yacco2::ast_postfix_1forest:: exec(){ @; } @*2 |ast_postfix_1forest| advance. @+= void yacco2::ast_postfix_1forest:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { AST* down = AST::get_1st_son(*base_stk_.cur_stk_rec_->node_); if (down == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit;//bypass left return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::left; base_stk_.push(*down,ast_base_stack::init); return; } case ast_base_stack::left: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::visit: { AST* rt(0); if(base_stk_.idx_ != 0) // only traverse the forest rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc;//bypass return; } base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_prefix_1forest|.\fbreak Forest prefix walk. Do not go outside its bounds. @+= yacco2::ast_prefix_1forest:: ast_prefix_1forest(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @ |ast_prefix_1forest| exec. @+= void yacco2::ast_prefix_1forest:: exec(){ @; } @*2 |ast_prefix_1forest| advance. @+= void yacco2::ast_prefix_1forest:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::left: { AST* rt(0); if(base_stk_.idx_ != 0) // only traverse the forest rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::visit: { AST* lt = AST::get_1st_son(*base_stk_.cur_stk_rec_->node_); if(lt != 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::left; base_stk_.push(*lt,ast_base_stack::init); return; } AST* rt(0); if(base_stk_.idx_ != 0) // only traverse the forest rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if(rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_breadth_only|.\fbreak Walk self and its younger brothers. @+= yacco2::ast_breadth_only:: ast_breadth_only(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @ |ast_breadth_only| exec. @+= void yacco2::ast_breadth_only:: exec(){ @; } @*2 |ast_breadth_only| advance. @+= void yacco2::ast_breadth_only:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::left: { AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::visit: { AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if(rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_prefix_wbreadth_only|.\fbreak Walk self who is a parent and its immediate children. @+= yacco2::ast_prefix_wbreadth_only:: ast_prefix_wbreadth_only(AST& Forest,Type_AST_functor* Action ,yacco2::int_set_type* Filter,bool Accept_opt) :yacco2::ast_stack(Action,Filter,Accept_opt){ base_stk_.push(Forest,ast_base_stack::init); } @ |ast_prefix_wbreadth_only| exec. @+= void yacco2::ast_prefix_wbreadth_only:: exec(){ @; } @*2 |ast_prefix_wbreadth_only| advance. @+= void yacco2::ast_prefix_wbreadth_only:: advance(){ if (base_stk_.cur_stk_rec_ == 0) return; switch (base_stk_.cur_stk_rec_->act_) { case ast_base_stack::init: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::visit; return; } case ast_base_stack::left: { if(base_stk_.idx_ == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if (rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::visit: { if(base_stk_.idx_ == 0){ AST* lt = AST::get_1st_son(*base_stk_.cur_stk_rec_->node_); if(lt == 0){ base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::left; base_stk_.push(*lt,ast_base_stack::init); return; } AST* rt = AST::brother(*base_stk_.cur_stk_rec_->node_); if(rt != 0){ base_stk_.pop(); base_stk_.push(*rt,ast_base_stack::init); return; } base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::right: { base_stk_.cur_stk_rec_->act_ = ast_base_stack::eoc; return; } case ast_base_stack::eoc: { base_stk_.pop(); return; } } } @*2 |ast_moonwalk_looking_for_ancestors|.\fbreak @+= yacco2::ast_moonwalk_looking_for_ancestors::ast_moonwalk_looking_for_ancestors (AST& Moonchild,USINT Goal, Type_AST_ancestor_list& Ancestors_list,Type_AST_functor* Functor ,yacco2::int_set_type* Filter,bool Accept_opt) :moonchild_(&Moonchild) ,goal_(Goal) ,ancestor_list_(&Ancestors_list) ,functor_(Functor) ,filter_(Filter) ,filter_type_(Accept_opt) ,filter_provided_(NO) { if(Filter != 0) filter_provided_ = YES; } @*3 |let_s_functor|.\fbreak It's returned value indicates either stop the tree walk, or continue the walk and what to do with the visited node --- accept it or bypass. @+= yacco2::functor_result_type yacco2::ast_moonwalk_looking_for_ancestors::let_s_functor(AST* Parent) { functor_result_type functor_result; yacco2::ast_base_stack abs; abs.push(*Parent,ast_base_stack::init); return functor_->operator()(&abs); } @*3 |deal_with_functor|.\fbreak If the Parent passes the grade it's added to the ancestry list. Returning a ``NO'' indicates to terminate the tree walking while a ``YES'' is keep-it-going thriller. @+= bool yacco2::ast_moonwalk_looking_for_ancestors::deal_with_functor(AST* Parent) { if(functor_ != 0){ functor_result_type functor_result = let_s_functor(Parent); switch(functor_result){ case accept_node:{ ancestor_list_->push_back(Parent); return YES; } case bypass_node:{ return YES; } case stop_walking:{ return NO; } } }else{ ancestor_list_->push_back(Parent); return YES; } return YES; } @*3 |let_s_moonwalk|.\fbreak Do those backward moves on the tree like MJ. @+= void yacco2::ast_moonwalk_looking_for_ancestors::let_s_moonwalk() { functor_result_type functor_result; AST* cnode = moonchild_; AST* parent(0); while (cnode!=0){ parent = AST::get_parent(*cnode); bool continue_waldo = deal_with_parent(parent); if(continue_waldo == NO) return; cnode = parent; } } @*3 |deal_with_parent|.\fbreak Returning a ``NO'' indicates to terminate the tree walking. @+= bool yacco2::ast_moonwalk_looking_for_ancestors::deal_with_parent(AST* Parent) { if(Parent == 0){// orphan? return NO; } CAbs_lr1_sym* tsym = AST::content(*Parent); USINT id = tsym->enumerated_id(); if(id == goal_){ ancestor_list_->push_back(Parent); return NO;// finished going thru tree as goal found } @; no_filter_so_accept_all_Tes:{ return deal_with_functor(Parent); } filtered_Tes:{ int_set_iter_type i = filter_->find(id); if(i == filter_->end()){ if(filter_type_== ACCEPT_FILTER) return YES; return deal_with_functor(Parent); } // found T in filter if(filter_type_== BYPASS_FILTER) return YES; return deal_with_functor(Parent); } } @ Dispatch on use-of-filter. @+= if(filter_provided_ == NO) goto no_filter_so_accept_all_Tes; else goto filtered_Tes; @** Build and restructure trees. @*2 |restructure_2trees_into_1tree|.. @+= yacco2::AST* yacco2::AST:: restructure_2trees_into_1tree(AST& S1,AST& S2){ AST* s2lt = AST::get_1st_son(S2); AST::zero_1st_son(S2); AST::crt_tree_of_2sons(S2,S1,*s2lt); return &S2; } @*2 Create trees |crt_tree_of_1son|---|crt_tree_of_9sons|. @+= void yacco2::AST:: crt_tree_of_1son(yacco2::AST& Parent,yacco2::AST& S1){ yacco2::AST::join_pts(Parent,S1); } void yacco2::AST:: crt_tree_of_2sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); } void yacco2::AST:: crt_tree_of_3sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); } void yacco2::AST:: crt_tree_of_4sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3 ,yacco2::AST& S4){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); yacco2::AST::join_sts(S3,S4); } void yacco2::AST:: crt_tree_of_5sons(yacco2::AST& Parent,AST& S1,yacco2::AST& S2,yacco2::AST& S3, yacco2::AST& S4,yacco2::AST& S5){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); yacco2::AST::join_sts(S3,S4); yacco2::AST::join_sts(S4,S5); } void yacco2::AST:: crt_tree_of_6sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3 ,yacco2::AST& S4,yacco2::AST& S5,yacco2::AST& S6){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); yacco2::AST::join_sts(S3,S4); yacco2::AST::join_sts(S4,S5); yacco2::AST::join_sts(S5,S6); } void yacco2::AST:: crt_tree_of_7sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3 ,yacco2::AST& S4,yacco2::AST& S5,yacco2::AST& S6,yacco2::AST& S7){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); yacco2::AST::join_sts(S3,S4); yacco2::AST::join_sts(S4,S5); yacco2::AST::join_sts(S5,S6); yacco2::AST::join_sts(S6,S7); } void yacco2::AST:: crt_tree_of_8sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3 ,yacco2::AST& S4,yacco2::AST& S5,yacco2::AST& S6,yacco2::AST& S7,yacco2::AST& S8){ yacco2::AST::join_pts(Parent,S1); yacco2::AST::join_sts(S1,S2); yacco2::AST::join_sts(S2,S3); yacco2::AST::join_sts(S3,S4); yacco2::AST::join_sts(S4,S5); yacco2::AST::join_sts(S5,S6); yacco2::AST::join_sts(S6,S7); yacco2::AST::join_sts(S7,S8); } void yacco2::AST:: crt_tree_of_9sons(yacco2::AST& Parent,yacco2::AST& S1,yacco2::AST& S2,yacco2::AST& S3 ,yacco2::AST& S4,yacco2::AST& S5,yacco2::AST& S6 ,yacco2::AST& S7,yacco2::AST& S8,yacco2::AST& S9){ AST::join_pts(Parent,S1); AST::join_sts(S1,S2); AST::join_sts(S2,S3); AST::join_sts(S3,S4); AST::join_sts(S4,S5); AST::join_sts(S5,S6); AST::join_sts(S6,S7); AST::join_sts(S7,S8); AST::join_sts(S8,S9); } @*2 |content| of node. @+= yacco2::CAbs_lr1_sym* yacco2::AST:: content(yacco2::AST& Node){ return Node.obj_; } @*2 |zero_1st_son| link. @+= void yacco2::AST:: zero_1st_son(yacco2::AST& Node){ Node.lt_ = 0; } @*2 |zero_2nd_son| link. @+= void yacco2::AST:: zero_2nd_son(yacco2::AST& Node){ yacco2::AST* lt = Node.lt_; if(lt == 0) { @.Err zero\_2nd\_son 2nd son's 1st...@> yacco2::KCHARP msg = "zero_2nd_son 2nd son's 1st son Node ptr is zero"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } lt->rt_ = 0; } @*2 |zero_brother| link. @+= void yacco2::AST:: zero_brother(yacco2::AST& Node){ Node.rt_ = 0; } @*2 |zero_content|. @+= void yacco2::AST:: zero_content(yacco2::AST& Node){ Node.obj_ = 0; } @*2 |set_content| of node. @+= void yacco2::AST:: set_content(yacco2::AST& Node,yacco2::CAbs_lr1_sym& Sym){ Node.obj_ = &Sym; } @*2 |zero_previous| link. @+= void yacco2::AST:: zero_previous(yacco2::AST& Node){ Node.pr_ = 0; } @*2 |set_content_wdelete|: mark node's content to be deleted when node deleted. @+= void yacco2::AST:: set_content_wdelete(yacco2::AST& Node,yacco2::CAbs_lr1_sym& Sym){ Node.obj_ = &Sym; Node.wdelete_ = true; } @*2 |set_previous| link. @+= void yacco2::AST:: set_previous(yacco2::AST& Node,yacco2::AST& Previous_node){ Node.pr_ = &Previous_node; } @*2 |wdelete| is node's contents marked as to-be-deleted?. @+= bool yacco2::AST:: wdelete(yacco2::AST& Node){ return Node.wdelete_; } @*2 |wdelete| set delete attribute: true or false. @+= void yacco2::AST:: wdelete(yacco2::AST& Node,bool Wdelete){ Node.wdelete_ = Wdelete; } @*2 Fetch various tree nodes: |brother|. @+= yacco2::AST* yacco2::AST:: brother(yacco2::AST& Node){ return Node.rt_; } @*2 |previous| node: returns its heritage parent or older brother.\fbreak Returns either the older brother or parent if the brother is first in the chain. A root node returns NIL. The difference between |previous| and |get_older_sibling| is in how it treats the oldest brother node. |get_older_sibling| does not return its parent node but returns NIL. @+= yacco2::AST* yacco2::AST:: previous(yacco2::AST& Node){ return Node.pr_; } @*2 Birth, pruning, and death of a tree node: |AST|. @+= yacco2::AST:: AST():lt_(0),rt_(0),pr_(0),obj_(0),wdelete_(false){ } yacco2::AST:: AST(yacco2::CAbs_lr1_sym& Obj):lt_(0),rt_(0),pr_(0),obj_(&Obj),wdelete_(false){ } yacco2::AST::~AST(){ if(wdelete_ == true) { delete obj_; } } @*2 |join_pts|: parent to son bonding. @+= void yacco2::AST:: join_pts(yacco2::AST& Parent,yacco2::AST& Child){ if (Parent.lt_ != 0) { @.Err join\_pts Parent lt ptr not ...@> yacco2::KCHARP msg = "join_pts Parent lt ptr not zero"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Parent == &Child) { @.Err join\_pts Parent and child n...@> yacco2::KCHARP msg = "join_pts Parent and child nodes are the same"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } Parent.lt_ = &Child; Child.pr_ = &Parent; } @*2 |join_sts|: brother to brother bonding. @+= void yacco2::AST:: join_sts(yacco2::AST& Elder_sibling,yacco2::AST& Younger_sibling){ if (Elder_sibling.rt_ != 0){ @.Err join\_sts Elder\_sibling rt ...@> yacco2::KCHARP msg = "join_sts Elder_sibling rt ptr not zero"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Elder_sibling == &Younger_sibling){ @.Err join\_sts Left and Right nod...@> yacco2::KCHARP msg = "join_sts Left and Right nodes are the same"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } Elder_sibling.rt_ = &Younger_sibling; Younger_sibling.pr_ = &Elder_sibling; } @*2 |ast_delete|: delete the tree node. @+= void yacco2::AST:: ast_delete(yacco2::AST& Node,bool Due_to_abort){ if(YACCO2_T__ != 0){ @; yacco2::lrclog << "YACCO2_T__::ast_DELETE Node to be deleted*: " << &Node << " Abort switch: " << Due_to_abort << __FILE__ << __LINE__<< std::endl; @; } if (&Node == Node.lt_){ @.Err ast\_delete recursion to sel...@> yacco2::KCHARP msg = "ast_delete recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Node == Node.rt_) { @.Err ast\_delete Right recursion ...@> yacco2::KCHARP msg = "ast_delete Right recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } yacco2::CAbs_lr1_sym* sym = Node.obj_; if(YACCO2_T__ != 0){ if(sym != 0){ @; yacco2::lrclog << "YACCO2_T__::ast_DELETE Node to be deleted*: " << &Node << " sym*: " << sym << " id: " << sym->id__ << __FILE__ << __LINE__<< std::endl; @; } } if(YACCO2_T__ != 0){ if(Node.lt_){ @; yacco2::lrclog << "YACCO2_T__::call ast_DELETE Node by LEFT node to be deleted*: " << Node.lt_ << " by node*: " << &Node << __FILE__ << __LINE__<< std::endl; @; AST::ast_delete(*Node.lt_,Due_to_abort); } if(Node.rt_){ @; yacco2::lrclog << "call ast_DELETE Node by RIGHT node to be deleted*: " << Node.rt_ << " by node*: " << &Node << __FILE__ << __LINE__<< std::endl; @; AST::ast_delete(*Node.rt_,Due_to_abort); } } if(sym != 0){// is there a sym to work on. if the delete process // was originally started by delete sym is 0 if(Due_to_abort == true){ if(sym->affected_by_abort() == true){ if(YACCO2_T__ != 0){ @; yacco2::lrclog << "YACCO2_T__::ast_DELETE node's object deleted due to ABORT: " << sym->id__ << __FILE__ << __LINE__<< std::endl; @; } delete sym; // protects against recycled bin deleting its items Node.obj_ = 0; }; }else{// normal throes of death delete sym; Node.obj_ = 0; } } delete &Node; @; lrclog << "ast_DELETE Node deleted*: " << &Node << __FILE__ << __LINE__<< std::endl; @; } @*2 |find_depth|. @+= yacco2::AST* yacco2::AST:: find_depth(AST& Node,yacco2::INT Enum){ if (&Node == Node.lt_){ @.Err find\_depth Left recursion t...@> yacco2::KCHARP msg = "find_depth Left recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Node == Node.rt_){ @.Err find\_depth Right recursion ...@> yacco2::KCHARP msg = "find_depth Right recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (Node.obj_ == 0){ @.Err find\_depth Tree's oject is ...@> yacco2::KCHARP msg = "find_depth Tree's oject is zero"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } yacco2::CAbs_lr1_sym* sym = Node.obj_; if (sym->enumerated_id__ == Enum) return &Node; if (Node.lt_ != 0) { yacco2::AST* rtn = find_depth(*Node.lt_,Enum); if (rtn != 0) return rtn; } if (Node.rt_ != 0) { yacco2::AST* rtn = find_depth(*Node.rt_,Enum); if (rtn!=0) return rtn; } return 0; } @*2 |find_breadth|. @+= yacco2::AST* yacco2::AST:: find_breadth(yacco2::AST& Node,yacco2::INT Enum){ if (&Node == Node.lt_){ @.Err find\_breadth Left recursion...@> yacco2::KCHARP msg = "find_breadth Left recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Node == Node.rt_){ @.Err find\_breadth Right recursio...@> yacco2::KCHARP msg = "find_breadth Right recursion to self Node"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (Node.obj_ == 0){ @.Err find\_breadth Tree's object ...@> yacco2::KCHARP msg = "find_breadth Tree's object is zero"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } yacco2::CAbs_lr1_sym* sym = Node.obj_; if (sym->enumerated_id__ == Enum) return &Node; if (Node.rt_ != 0) { yacco2::AST* rtn = find_breadth(*Node.rt_,Enum); if (rtn!=0) return rtn; } return 0; } @** Tree relinking routines: before, between, after and other sundries. @*2 |relink|.\fbreak This drops the old link and re-welds the previous node to the new node. The relationships between the previous and old node are erased. No memory meltdown but pure lobotomy with 2 scoops. @+= void yacco2::AST:: relink(yacco2::AST& Previous,yacco2::AST& Old_to,yacco2::AST& New_to){ if (&Previous == &Old_to){ @.Err relink Previous ptr == Old p...@> yacco2::KCHARP msg = "relink Previous ptr == Old ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Previous == &New_to){ @.Err relink Previous ptr == New p...@> yacco2::KCHARP msg = "relink Previous ptr == New ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Old_to == &New_to){ @.Err relink Old ptr == New ptr@> yacco2::KCHARP msg = "relink Old ptr == New ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if(Previous.rt_ == &Old_to){ Old_to.pr_ = 0; Previous.rt_ = &New_to; New_to.pr_ = &Previous; return; } Old_to.pr_ = 0; Previous.lt_ = &New_to; New_to.pr_ = &Previous; } @*2 |relink_between|.\fbreak This wedges the new node inbetween the previous and old node. Depending on the relationship between the previous and old node, the same relationship is maintained; this can be parental or brotherly love. The new node becomes the older brother to the old node. @+= void yacco2::AST:: relink_between(yacco2::AST& Previous,yacco2::AST& Old_to,yacco2::AST& New_to){ if (&Previous == &Old_to){ @.Err relink\_between Previous ptr...@> yacco2::KCHARP msg = "relink_between Previous ptr == Old ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Previous == &New_to){ @.Err relink\_between Previous ptr...@> yacco2::KCHARP msg = "relink_between Previous ptr == New ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if (&Old_to == &New_to){ @.Err relink\_between Old ptr == N...@> yacco2::KCHARP msg = "relink_between Old ptr == New ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if(Previous.rt_ == &Old_to){ Old_to.pr_ = &New_to; Previous.rt_ = &New_to; New_to.pr_ = &Previous; New_to.rt_ = &Old_to; return; } if(Previous.lt_ == &Old_to){ Old_to.pr_ = &New_to; Previous.lt_ = &New_to; New_to.pr_ = &Previous; New_to.rt_ = &Old_to; return; } @.Err relink\_between Previous nod...@> yacco2::KCHARP msg = "ast_relink_between Previous node does not have lt or rt of Old"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } @*2 |relink_after|.\fbreak This adds the new node as the previous node's immediate younger brother. If there was a younger brother already established, it re-aligns these relations. There is no politeness; just raw butting in. @+= void yacco2::AST:: relink_after(yacco2::AST& Previous,yacco2::AST& To){ if (&Previous == &To){ @.Err relink\_after Previous ptr==...@> yacco2::KCHARP msg = "relink_after Previous ptr == To ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if(Previous.rt_ == 0){// eoc Previous.rt_ = &To; To.pr_ = &Previous; return; } AST* rt = Previous.rt_; if(rt->pr_ == &Previous){ rt->pr_ = &To; Previous.rt_ = &To; To.pr_ = &Previous; To.rt_ = rt; return; } @.Err relink\_after Previous Node ...@> yacco2::KCHARP msg = "relink_after Previous Node does not have lt or rt of Old"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } @*2 |relink_before|.\fbreak The new node is added before the `Before' node. Depending on the Before's node relationship as either the oldest child or a younger sibling, |relink_before| maintains this relationship with the |New_to| node while the `Before' node becomes |New_to|'s younger brother. @+= void yacco2::AST:: relink_before(yacco2::AST& Before,yacco2::AST& New_to){ if (&Before == &New_to){ @.Err relink\_before Before ptr ==...@> yacco2::KCHARP msg = "relink_before Before ptr == New ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } if(Before.pr_ == 0){//eoc Before.pr_ = &New_to; New_to.rt_ = &Before; return; } yacco2::AST* pr = Before.pr_; if(pr->lt_ == &Before){ pr->lt_ = &New_to; New_to.pr_ = pr; New_to.rt_ = &Before; Before.pr_ = &New_to; return; } if(pr->rt_ == &Before){ pr->rt_ = &New_to; New_to.pr_ = pr; New_to.rt_ = &Before; Before.pr_ = &New_to; return; } @.Err relink\_before Before node d...@> yacco2::KCHARP msg = "relink_before Before node does not have lt or rt of Old"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } @*2 |replace_node|.\fbreak Substitute the Old node with the By node. Remap all the relations to the By node and wipe out relationships in Old node leaving it as an orphan. @+= void yacco2::AST:: replace_node(yacco2::AST& Old,yacco2::AST& By){ if (&Old == &By){ @.Err replace\_node Old ptr == By ...@> yacco2::KCHARP msg = "replace_node Old ptr == By ptr"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } yacco2::AST* prev = Old.pr_; yacco2::AST* rt = Old.rt_; if(prev->rt_ == &Old){ prev->rt_ = &By; By.pr_ = prev; By.rt_ = rt; if(rt != 0) rt->pr_ = &By; Old.rt_ = 0; Old.pr_ = 0; return; } if(prev->lt_ == &Old){ prev->lt_ = &By; By.pr_ = prev; By.rt_ = rt; if(rt != 0) rt->pr_ = &By; Old.rt_ = 0; Old.pr_ = 0; return; } By.rt_ = Old.rt_; Old.rt_ = 0; } @** Various tree node routines. @*2 |add_son_to_tree|.\fbreak Just wedge the new kid as an oldest child with the Parent node. If the Parent node is childless... well congratulations. If there are already children, well let the probate officer deal with the squawkes. @+= void yacco2::AST:: add_son_to_tree(yacco2::AST& Parent,yacco2::AST& Son){ AST* p_lt = Parent.lt_; if(p_lt == 0){ Parent.lt_ = &Son; Son.pr_ = &Parent; return; } Parent.lt_ = &Son; Son.pr_ = &Parent; Son.rt_ = p_lt; p_lt->pr_ = &Son; } @*2 |add_child_at_end|. @+= yacco2::AST* yacco2::AST::add_child_at_end(yacco2::AST& Tree,yacco2::AST& Child){ yacco2::AST* cur_youngest_child = AST::get_child_at_end(Tree); if(cur_youngest_child == 0){ AST::join_pts(Tree,Child); }else{ AST::join_sts(*cur_youngest_child,Child); } return &Child; } @*2 |get_spec_child|. @+= yacco2::AST* yacco2::AST::get_spec_child(yacco2::AST& Tree,yacco2::INT Cnt){ if(Cnt <= 0) { @.Err get\_spec\_child Node Cnt is...@> yacco2::KCHARP msg = "get_spec_child Node Cnt is <= 0"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } yacco2::INT pos(0); yacco2::AST* ct = Tree.lt_; for(;ct != 0;ct=ct->rt_){ ++pos; if(pos == Cnt) return ct; } return 0; } @*2 Get specific son node by number. @+= yacco2::AST* yacco2::AST:: get_1st_son(yacco2::AST& Node){ return get_spec_child(Node,1); } yacco2::AST* yacco2::AST:: get_2nd_son(yacco2::AST& Node){ return get_spec_child(Node,2); } yacco2::AST* yacco2::AST:: get_3rd_son(yacco2::AST& Node){ return get_spec_child(Node,3); } yacco2::AST* yacco2::AST:: get_4th_son(yacco2::AST& Node){ return get_spec_child(Node,4); } yacco2::AST* yacco2::AST:: get_5th_son(yacco2::AST& Node){ return get_spec_child(Node,5); } yacco2::AST* yacco2::AST::get_6th_son(yacco2::AST& Node){ return get_spec_child(Node,6); } yacco2::AST* yacco2::AST::get_7th_son(yacco2::AST& Node){ return get_spec_child(Node,7); } yacco2::AST* yacco2::AST::get_8th_son(yacco2::AST& Node){ return get_spec_child(Node,8); } yacco2::AST* yacco2::AST::get_9th_son(yacco2::AST& Node){ return get_spec_child(Node,9); } @*2 |get_child_at_end|. Go thru the parent's children looking for the youngest. @+= yacco2::AST* yacco2::AST::get_child_at_end(yacco2::AST& Tree){ yacco2::AST* ct = Tree.lt_; yacco2::AST* pct(0); for(;ct != 0;ct=ct->rt_){ pct = ct; } return pct; } @*2 |get_youngest_sibling|.\fbreak If there is no younger brother then a |NIL| pointer is returned indicating such condition. It is up to the user to check the validity. @+= yacco2::AST* yacco2::AST::get_youngest_sibling(yacco2::AST& Tree){ yacco2::AST* start = &Tree; yacco2::AST* younger_sibling = start; for(;younger_sibling != 0;){ if(younger_sibling->rt_ == 0) break; younger_sibling = younger_sibling->rt_; } if(start == younger_sibling) return 0; return younger_sibling; } @*2 |get_younger_sibling|.\fbreak It goes right along the brother chain looking for the brother x youngest to him. @+= yacco2::AST* yacco2::AST::get_younger_sibling(yacco2::AST& Child,yacco2::INT Pos){ if(Pos <= 0) { @.Err get\_younger\_sibling Pos <=...@> yacco2::KCHARP msg = "get_younger_sibling Pos <= 0"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } int cnt(0); yacco2::AST* younger_sibling = Child.rt_; for(;younger_sibling != 0;younger_sibling=younger_sibling->rt_){ ++cnt; if(cnt == Pos) return younger_sibling; } return 0; } @*2 |get_older_sibling|: returns only older brother.\fbreak It goes to its left along the brother chain in older order. If it is the first in the breadth chain, well, it's the end and returns a nil ptr. @+= yacco2::AST* yacco2::AST::get_older_sibling(yacco2::AST& Child,yacco2::INT Pos){ if(Pos >= 0) { @.Err get\_older\_sibling Pos >= 0@> yacco2::KCHARP msg = "get_older_sibling Pos >= 0"; Yacco2_faulty_precondition(msg,__FILE__,__LINE__); exit(1); } int cnt(0); AST* older_sibling = Child.pr_; for(;older_sibling != 0;older_sibling=older_sibling->pr_){ --cnt; if(cnt == Pos) return older_sibling; } return 0; } @*2 |get_parent|: child guidance required.\fbreak @+= yacco2::AST* yacco2::AST::get_parent(yacco2::AST& Tree){ yacco2::AST* cnode = &Tree; yacco2::AST* older_sibling = cnode->pr_; for(;older_sibling != 0;cnode = older_sibling,older_sibling=cnode->pr_){ if(older_sibling->rt_ != cnode) return older_sibling;// } return 0; } @*2 |common_ancestor|: Are we distant ?.\fbreak @+= yacco2::AST* yacco2::AST::common_ancestor (yacco2::Type_AST_ancestor_list& ListA,yacco2::Type_AST_ancestor_list& ListB){ Type_AST_ancestor_list* a; Type_AST_ancestor_list* b; if(ListA.size() < ListB.size()){ a = &ListA; b = &ListB; }else{ b = &ListA; a = &ListB; } Type_AST_ancestor_list::iterator ai = a->begin(); Type_AST_ancestor_list::iterator aie = a->end(); Type_AST_ancestor_list::iterator bi; Type_AST_ancestor_list::iterator bie; for(;ai!=aie;++ai){ bi = b->begin(); bie = b->end(); for(;bi!=bie;++bi){ AST* A = *ai; AST* B = *bi; if(A == B) return A; } } return 0; } @*2 |divorce_node_from_tree|.\fbreak Never pretty but civil in its settlement. @+= yacco2::AST* yacco2::AST::divorce_node_from_tree(yacco2::AST& Node){ yacco2::AST* bpr = Node.pr_; yacco2::AST* brt = Node.rt_; @; @; forest:@/ @; amongst_brothers:@/ @; parental_guidance:@/ @; } @ Dispatch to node association.\fbreak The following points are the sequences checked on the removed node's relationship within the tree structure.\fbreak \ptindent{1) forest --- only node or oldest node in the forest} \ptindent{2) middle or youngest node in the forest} \ptindent{3) parent with one or more children} @= if(bpr == 0) goto forest; if(bpr->rt_ == &Node) goto amongst_brothers; if(bpr->lt_ == &Node) goto parental_guidance; @ Handle parent / sibling relationship. Is it an only child? If so, then remove the parent relationship. If there are brothers, then re-align the relationships in both the parent and the younger child. @= @; @; @ Only child? yes make parent childless and exit. @= if(brt == 0){ bpr->lt_ = 0; return 0; } @*2 Re-bond younger child with parent. @= bpr->lt_ = brt; brt->pr_ = bpr; return brt; @ Handle a forest situation, with or without a younger brother.\fbreak It is considered a forest if there is no older brother attached to it. If it has a brother, disconnect the younger brother's association from he removed node, and back back the younger brother. @= if(brt == 0) return 0; // onlynode; brt->pr_ = 0; return brt; @ Handle sibling relationship. This situation is:\fbreak a ${\rightarrow b \rightarrow ?}$ where ? is either nil or a node. So, relink node a with its younger brother c. @= bpr->rt_ = brt; if(brt != 0) brt->pr_ = bpr; return brt; @ Remove node's association from tree. @= Node.pr_ = 0; Node.rt_ = 0; @*2 |clone_tree|.\fbreak logic: walk the tree in prefix\fbreak Ip:\fbreak \ptindent{1) To - node to copy} \ptindent{2) Calling - predecessor: if 0, no predecesor. it's the root} \ptindent{3) Relation - join options: init,left,right} Op:\fbreak \ptindent{new tree with each new node's content being a duplicate} The new tree is a complete copy. The tree nodes are fresh from the malloc bakery with their contents being the same. @+= yacco2::AST* yacco2::AST::clone_tree (yacco2::AST& Node_to_copy,yacco2::AST* Calling_node ,yacco2::ast_base_stack::n_action Relation){ yacco2::AST* new_t = new yacco2::AST(*yacco2::AST::content(Node_to_copy));// copy node switch(Relation){// how to join case ast_base_stack::init: break;// root case ast_base_stack::left:{ if(Calling_node != 0){ AST::join_pts(*Calling_node,*new_t); } break; } case ast_base_stack::right:{ if(Calling_node != 0){ AST::join_sts(*Calling_node,*new_t); } break; } } if(Node_to_copy.lt_ != 0) AST::clone_tree(*Node_to_copy.lt_,new_t,ast_base_stack::left); if(Node_to_copy.rt_ != 0) AST::clone_tree(*Node_to_copy.rt_,new_t,ast_base_stack::right); return new_t; } @** Some tree functors: remove, insert back, print a tree, etc.\fbreak These functors are examples of how to create your own functor. |prt_ast_functor| prints out a tree in indented format. |fire_a_func_functor| just calls a procedure passing it the current tree node. |str_ast_functor| claim to fame is in its use of the |BYPASS_FILTER| given the many abstract meta-terminal that parent each subtree: for example the Pascal railroad diagrams with expression, simple expression, term, factor, etc. Depending on how abstract u make the tree, there are still parent nodes that u might not want to see. |str_ast_functor| builds a source string from an tree used in a Pascal translator from Oregon to HP Pascal source code retargeting.\fbreak \fbreak An improvement: the address of the functor is passed to the call-back function so that is can also act as a container. The reason behind this is the |str_ast_functor|. It orginally had a global string for the function to fill. As the functor is the driver of the call-back, it is the one that knows when the source string should be cleared for reuse. @+= struct insert_back_recycled_items_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); void insert_node(yacco2::AST& Inode); yacco2::AST* new_root(); void insert_before(); private:@/ yacco2::ast_base_stack* stk_env_; yacco2::INT idx_; yacco2::AST* cnode_; yacco2::ast_base_stack::s_rec* srec_; yacco2::AST* insert_node_; yacco2::AST* new_root_; }; @*2 |tok_can_ast_functor|. @+= struct tok_can_ast_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); }; @*2 |tok_can_ast_no_stop_functor|. @+= struct tok_can_ast_no_stop_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); }; @*2 |tok_can_ast_bypass_functor|. @+= struct tok_can_ast_bypass_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); }; @*2 |prt_ast_functor|. @+= struct prt_ast_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); typedef void (*PF)(AST*); prt_ast_functor(PF Func,std::ofstream* Ofile=0); void reset_cnt(); private:@/ yacco2::ast_base_stack* stk_env_; yacco2::INT idx_; yacco2::AST* cnode_; yacco2::ast_base_stack::s_rec* srec_; PF prt_funct_; yacco2::INT cnt_; char how_[3]; std::ofstream* ofile_; }; @*2 |fire_a_func_ast_functor|. @+= struct fire_a_func_ast_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); typedef void (*PF)(AST*); fire_a_func_ast_functor(PF Func); private:@/ yacco2::ast_base_stack* stk_env_; yacco2::INT idx_; yacco2::AST* cnode_; yacco2::ast_base_stack::s_rec* srec_; PF a_funct_; }; @*2 |str_ast_functor| --- build up source string. @+= struct str_ast_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); typedef void (*PF)(AST*,Type_AST_functor*); str_ast_functor(PF Func); std::string source_str_; private:@/ yacco2::ast_base_stack* stk_env_; yacco2::INT idx_; yacco2::AST* cnode_; yacco2::ast_base_stack::s_rec* srec_; PF prt_funct_; char how_[3]; }; @*2 |remove_unwanted_ast_functor|. @+= struct remove_unwanted_ast_functor :public Type_AST_functor{ functor_result_type operator()(yacco2::ast_base_stack* Stk_env); void possible_delete(); ~remove_unwanted_ast_functor(); private:@/ yacco2::ast_base_stack* stk_env_; yacco2::INT idx_; yacco2::AST* cnode_; yacco2::ast_base_stack::s_rec* srec_; }; @*2 Implementation of some functors. |remove_unwanted_ast_functor|. @+= yacco2::functor_result_type yacco2::remove_unwanted_ast_functor::operator()(yacco2::ast_base_stack* Stk_env){ stk_env_ = Stk_env; srec_ = stk_env_->cur_stk_rec_; idx_ = stk_env_->idx_; cnode_ = srec_->node_; yacco2::CAbs_lr1_sym* sobj = AST::content(*cnode_); if (sobj == 0) return accept_node; if (sobj->tok_co_ords__.external_file_id__ <= 1) return accept_node; idx_ = stk_env_->idx_; if (stk_env_->idx_ == 0){// 1st entry of complete tree return accept_node; } return bypass_node;// cuz: apple's symantic error } void yacco2::remove_unwanted_ast_functor::possible_delete(){ yacco2::INT pidx = idx_ - 1; if(pidx < 0) return; ast_base_stack::s_rec* psrec = stk_env_->stk_rec(pidx); yacco2::AST* psnode = psrec->node_; yacco2::AST* srt = AST::brother(*cnode_); switch (psrec->act_){ case ast_base_stack::left:{ if(srt != 0){// replace current record with rt node: shift left tree yacco2::AST::relink(*psnode,*cnode_,*srt); srec_->node_ = srt; srec_->act_ = ast_base_stack::init; return; } yacco2::AST::zero_1st_son(*psnode); srec_->act_ = ast_base_stack::eoc;// deleted node: complete its seq return; } case ast_base_stack::right:{ if(srt != 0){ yacco2::AST::relink(*psnode,*cnode_,*srt); srec_->node_ = srt; srec_->act_ = ast_base_stack::init; return; } yacco2::AST::zero_brother(*psnode); srec_->act_ = ast_base_stack::eoc;// deleted node: complete its seq return; } default:{ return; } } } yacco2::remove_unwanted_ast_functor::~remove_unwanted_ast_functor(){ } @*2 Insert items back into a tree. @+= yacco2::functor_result_type yacco2::insert_back_recycled_items_functor::operator()(yacco2::ast_base_stack* Stk_env){ stk_env_ = Stk_env; srec_ = stk_env_->cur_stk_rec_; idx_ = stk_env_->idx_; cnode_ = srec_->node_; yacco2::CAbs_lr1_sym* top_node_sym = AST::content(*cnode_); yacco2::CAbs_lr1_sym* node_sym = AST::content(*insert_node_); if(node_sym->tok_co_ords__.rc_pos__ <= top_node_sym->tok_co_ords__.rc_pos__) return accept_node; return bypass_node;// cuz: apple's symantic error } void yacco2::insert_back_recycled_items_functor::insert_node (yacco2::AST& Inode){insert_node_ = &Inode;} yacco2::AST* yacco2::insert_back_recycled_items_functor::new_root(){return new_root_;} void yacco2::insert_back_recycled_items_functor::insert_before(){ if(stk_env_->idx_ > 0) goto overlay; root_change:@/ new_root_ = insert_node_; overlay:@/ // overlay cur node with new node to insert srec_->node_ = insert_node_; srec_->act_ = ast_base_stack::right; AST::join_sts(*insert_node_,*cnode_); // adj visited node: default to visit cuz next ast could be < than it stk_env_->push(*cnode_,ast_base_stack::visit); adj_prev_caller:@/ if(stk_env_->idx_ == 0) return;// only root yacco2::INT pi = idx_ - 1; yacco2::ast_base_stack::s_rec* pcur_rec = stk_env_->stk_rec(pi); yacco2::AST* pnode = pcur_rec->node_; switch (pcur_rec->act_){ case yacco2::ast_base_stack::left:{ yacco2::AST::zero_1st_son(*pnode); yacco2::AST::join_pts(*pnode,*insert_node_); return; } case yacco2::ast_base_stack::right:{ yacco2::AST::zero_brother(*pnode); yacco2::AST::join_sts(*pnode,*insert_node_); return; } } return; } @*2 |tok_can_ast_functor| continue looping thru the tree. @+= yacco2::functor_result_type yacco2::tok_can_ast_functor::operator()(ast_base_stack* Stk_env){ return accept_node;// stop looping thru ast } @ |tok_can_ast_no_stop_functor| stop looping thru the tree. @+= yacco2::functor_result_type yacco2::tok_can_ast_no_stop_functor::operator()(ast_base_stack* Stk_env){ return stop_walking;//continue looping thru ast } @*2 |tok_can_ast_bypass_functor|. @+= yacco2::functor_result_type yacco2::tok_can_ast_bypass_functor::operator()(ast_base_stack* Stk_env){ yacco2::ast_base_stack::s_rec* srec = Stk_env->cur_stk_rec_; yacco2::AST* cnode = srec->node_; yacco2::CAbs_lr1_sym* sym = AST::content(*cnode); if(sym->tok_co_ords__.external_file_id__ > 1) return bypass_node;//contine the walk, not wanted return bypass_node; } @*2 |prt_ast_functor|. @+= yacco2::functor_result_type yacco2::prt_ast_functor::operator()(yacco2::ast_base_stack* Stk_env){ stk_env_ = Stk_env; srec_ = stk_env_->cur_stk_rec_; idx_ = stk_env_->idx_; yacco2::INT pidx = idx_ - 1; cnode_ = srec_->node_; //std::string how; if(pidx <= 0) goto prt_prefix; { ast_base_stack::s_rec* psrec = stk_env_->stk_rec(pidx); if(psrec->act_ == ast_base_stack::left){ how_[0]='l'; }else{ how_[0]='r'; } how_[1] = 't'; how_[2] = (char)0; } prt_prefix:@/ @; yacco2::INT no_lt(0); for (yacco2::INT x = 0; x <= idx_; ++x) if(stk_env_->stk_rec(x)->act_ == ast_base_stack::left) ++no_lt; for (yacco2::INT x = 0; x <= no_lt; ++x) (*ofile_) << " "; (*ofile_) << ++cnt_ << "::" << ' '; @; call_prt_func:@/ (*prt_funct_)(cnode_); return accept_node;//continue looping thru ast } yacco2::prt_ast_functor::prt_ast_functor(PF Func,std::ofstream* Ofile):prt_funct_(Func),cnt_(0){ if(Ofile == 0){ ofile_ = &yacco2::lrclog; }else{ ofile_ = Ofile; } } void yacco2::prt_ast_functor::reset_cnt(){ cnt_ = 0; } @*2 |fire_a_func_ast_functor|. @+= yacco2::functor_result_type yacco2::fire_a_func_ast_functor::operator()(yacco2::ast_base_stack* Stk_env){ stk_env_ = Stk_env; srec_ = stk_env_->cur_stk_rec_; idx_ = stk_env_->idx_; yacco2::INT pidx = idx_ - 1; cnode_ = srec_->node_; call_prt_func:@/ (*a_funct_)(cnode_); return accept_node;//continue looping thru ast } yacco2::fire_a_func_ast_functor::fire_a_func_ast_functor(PF Func):a_funct_(Func){} @*2 |str_ast_functor|. @+= yacco2::functor_result_type yacco2::str_ast_functor::operator()(yacco2::ast_base_stack* Stk_env){ stk_env_ = Stk_env; srec_ = stk_env_->cur_stk_rec_; idx_ = stk_env_->idx_; yacco2::INT pidx = idx_ - 1; cnode_ = srec_->node_; //std::string how; if(pidx <= 0) goto prt_prefix; { ast_base_stack::s_rec* psrec = stk_env_->stk_rec(pidx); if(psrec->act_ == ast_base_stack::left){ how_[0]='l'; }else{ how_[0]='r'; } how_[1] = 't'; how_[2] = (char)0; } prt_prefix:@/ call_prt_func:@/ (*prt_funct_)(cnode_,this); return accept_node;//continue looping thru ast } yacco2::str_ast_functor::str_ast_functor(PF Func):prt_funct_(Func){source_str_.clear();}