• R/O
  • SSH
  • HTTPS

eircompile: Commit


Commit MetaInfo

Revision70 (tree)
Time2022-01-03 02:23:52
Authorquiret

Log Message

- fixed a bug in the Eir lexer where preemptive-returns were not handled according to spec; now all preemptive returns use the proper logic
- added template-instantiation unit tests for COS
- fixed function-declaration handling related to locators and array-specs shrouding the funcsig type
- improved the performance even more thanks to the new Eir SDK revision

Change Summary

Incremental Difference

--- eircompile/include/eircompile/lexing_compile.h (revision 69)
+++ eircompile/include/eircompile/lexing_compile.h (revision 70)
@@ -177,6 +177,7 @@
177177 typename cenv_t::StepToken tokextended = &cenv;
178178 typename cenv_t::StepToken tokprocgate = &cenv;
179179 typename cenv_t::StepToken tokreplaceable = &cenv;
180+ typename cenv_t::StepToken toknpgate = &cenv;
180181 typename cenv_t::StepAlternatives callprodattrib = &cenv;
181182 typename cenv_t::SerializedStep callprodattrib_spacesappend = &cenv;
182183 typename cenv_t::StepRepeatByInterval opt_callprodattrib = &cenv;
@@ -1247,11 +1248,14 @@
12471248
12481249 tokreplaceable.token_str = _Token_Replaceable <charType> ();
12491250
1251+ toknpgate.token_str = _Token_NoProgressGate <charType> ();
1252+
12501253 callprodattrib.SetConnections( {
12511254 &tokinline,
12521255 &tokextended,
12531256 &tokprocgate,
1254- &tokreplaceable
1257+ &tokreplaceable,
1258+ &toknpgate
12551259 } );
12561260 callprodattrib.attribute_key = _AttribKey_Attribute <charType> ();
12571261
@@ -1259,7 +1263,7 @@
12591263
12601264 opt_callprodattrib.min_rep_cnt = 0;
12611265 opt_callprodattrib.has_maximum = true;
1262- opt_callprodattrib.max_rep_cnt = 4;
1266+ opt_callprodattrib.max_rep_cnt = 5;
12631267 opt_callprodattrib.SetNode( &callprodattrib_spacesappend );
12641268
12651269 callprod.SetConnections( { &opt_callprodattrib, &prodtoken } );
@@ -2017,6 +2021,30 @@
20172021 return U"replaceable";
20182022 }
20192023 }
2024+ template <eir::CharacterType charType>
2025+ static AINLINE const charType* _Token_NoProgressGate( void ) noexcept
2026+ {
2027+ if constexpr ( std::same_as <charType, char> )
2028+ {
2029+ return "npgate";
2030+ }
2031+ if constexpr ( std::same_as <charType, wchar_t> )
2032+ {
2033+ return L"npgate";
2034+ }
2035+ if constexpr ( std::same_as <charType, char8_t> )
2036+ {
2037+ return u8"npgate";
2038+ }
2039+ if constexpr ( std::same_as <charType, char16_t> )
2040+ {
2041+ return u"npgate";
2042+ }
2043+ if constexpr ( std::same_as <charType, char32_t> )
2044+ {
2045+ return U"npgate";
2046+ }
2047+ }
20202048
20212049 template <eir::CharacterType charType>
20222050 static AINLINE const charType* _Token_TakeFirst( void ) noexcept
@@ -2551,6 +2579,10 @@
25512579 {
25522580 this->replaceable = true;
25532581 }
2582+ else if ( prodName == _Token_NoProgressGate <charType> () )
2583+ {
2584+ this->npgate = true;
2585+ }
25542586 }
25552587 }
25562588 };
--- eircompile/include/eircompile/lexing_env.h (revision 69)
+++ eircompile/include/eircompile/lexing_env.h (revision 70)
@@ -616,6 +616,15 @@
616616 return false;
617617 }
618618
619+ // Returns true if this step is a no-progress-gate.
620+ // This is important for entering steps and correctly handling the no-progress-information
621+ // inheritance.
622+ virtual bool IsNoProgressGate( void ) const noexcept
623+ {
624+ // By default no step is a npgate.
625+ return false;
626+ }
627+
619628 LexingEnvironment *env;
620629
621630 private:
@@ -760,11 +769,13 @@
760769 this->is_procedural_gate = right.is_procedural_gate;
761770 this->base_on_extension = right.base_on_extension;
762771 this->replaceable = right.replaceable;
772+ this->npgate = right.npgate;
763773
764774 right.exec = nullptr;
765775 right.is_procedural_gate = false;
766776 right.base_on_extension = false;
767777 right.replaceable = false;
778+ right.npgate = false;
768779 }
769780
770781 ~StepAttribution( void )
@@ -812,6 +823,11 @@
812823 return 1;
813824 }
814825
826+ bool IsNoProgressGate( void ) const noexcept override
827+ {
828+ return this->npgate;
829+ }
830+
815831 private:
816832 Step *exec = nullptr;
817833 public:
@@ -818,6 +834,7 @@
818834 bool is_procedural_gate = false;
819835 bool base_on_extension = false; // look for additional extensions
820836 bool replaceable = false; // look for replacement
837+ bool npgate = false; // establish no-progress-gate
821838
822839 void SetNode( Step *node )
823840 {
@@ -850,8 +867,21 @@
850867 using Step::Step;
851868
852869 StepCall( const StepCall& right ) = default;
853- StepCall( StepCall&& ) = default;
870+ StepCall( StepCall&& right ) noexcept : production_name( std::move( right.production_name ) )
871+ {
872+ this->is_inline = right.is_inline;
873+ this->base_on_extension = right.base_on_extension;
874+ this->replaceable = right.replaceable;
875+ this->is_procedural_gate = right.is_procedural_gate;
876+ this->npgate = right.npgate;
854877
878+ right.is_inline = false;
879+ right.base_on_extension = false;
880+ right.replaceable = false;
881+ right.is_precedural_name = false;
882+ right.npgate = false;
883+ }
884+
855885 ~StepCall( void )
856886 {
857887 return;
@@ -882,6 +912,11 @@
882912 return 1;
883913 }
884914
915+ bool IsNoProgressGate( void ) const noexcept override
916+ {
917+ return this->npgate;
918+ }
919+
885920 private:
886921 DEFINE_HEAP_REDIR_ALLOC_BYSTRUCT( production_name_redirAlloc, allocatorType );
887922
@@ -891,8 +926,9 @@
891926 bool base_on_extension = false; // if true then we call the last extended-call instead of the base version.
892927 bool replaceable = false; // if true then this call will look for a replacement of the production first
893928 bool is_procedural_gate = false; // if true then a procedural step will have to start at 0 if this is the most-recent find.
929+ bool npgate = false; // if true then empty-reentrancy and result information is reset for enclosed productions
894930
895- void SetProductionName( const eir::FixedString <charType>& prodname )
931+ inline void SetProductionName( const eir::FixedString <charType>& prodname )
896932 {
897933 // nonterminal_namespace is safe because it is always root.
898934 this->production_name = prodname;
@@ -2928,7 +2964,10 @@
29282964 this->was_called_by_extend = right.was_called_by_extend;
29292965 this->attrib_prefix = std::move( right.attrib_prefix );
29302966 this->noprogress_map = std::move( right.noprogress_map );
2967+#ifdef _DEBUG
29312968 this->owns_noprogress_active_data = right.owns_noprogress_active_data;
2969+#endif //_DEBUG
2970+ this->is_npgated = right.is_npgated;
29322971 this->selectorMetaData_initialized = right.selectorMetaData_initialized;
29332972 this->metaData = right.metaData;
29342973 this->selectorMetaData = right.selectorMetaData;
@@ -2942,7 +2981,9 @@
29422981 right.is_attrib_continuation = false;
29432982 right.requires_init = false;
29442983 right.was_called_by_extend = false;
2984+#ifdef _DEBUG
29452985 right.owns_noprogress_active_data = false;
2986+#endif //_DEBUG
29462987 right.selectorMetaData_initialized = false;
29472988 right.metaData = nullptr;
29482989 right.selectorMetaData = nullptr;
@@ -2993,7 +3034,10 @@
29933034 this->was_called_by_extend = right.was_called_by_extend;
29943035 this->attrib_prefix = std::move( right.attrib_prefix );
29953036 this->noprogress_map = std::move( right.noprogress_map );
3037+#ifdef _DEBUG
29963038 this->owns_noprogress_active_data = right.owns_noprogress_active_data;
3039+#endif //_DEBUG
3040+ this->is_npgated = right.is_npgated;
29973041 this->selectorMetaData_initialized = right.selectorMetaData_initialized;
29983042 this->metaData = right.metaData;
29993043 this->selectorMetaData = right.selectorMetaData;
@@ -3007,7 +3051,9 @@
30073051 right.is_attrib_continuation = false;
30083052 right.requires_init = false;
30093053 right.was_called_by_extend = false;
3054+#ifdef _DEBUG
30103055 right.owns_noprogress_active_data = false;
3056+#endif //_DEBUG
30113057 right.selectorMetaData_initialized = false;
30123058 right.metaData = nullptr;
30133059 right.selectorMetaData = nullptr;
@@ -3278,7 +3324,10 @@
32783324 public:
32793325 eir::String <charType, attrib_prefix_redirAlloc> attrib_prefix;
32803326 eir::Set <noprogress_data, noprogress_map_redirAlloc, noprogress_data> noprogress_map;
3327+#ifdef _DEBUG
32813328 bool owns_noprogress_active_data = false;
3329+#endif //_DEBUG
3330+ bool is_npgated = false; // if true then the associated step is a npgate; thus it did not inherit no-progress info.
32823331 bool selectorMetaData_initialized = false;
32833332 void *metaData = nullptr; // meta-data pointer of the step in general.
32843333 void *selectorMetaData = nullptr; // if the assigned-to step has a node_selector, then this is used to hold its meta-data.
@@ -3840,11 +3889,13 @@
38403889 entry.current_step = start_step;
38413890 entry.process_idx = 0;
38423891 entry.pt = buf;
3892+#ifdef _DEBUG
38433893 entry.owns_noprogress_active_data = true;
3894+#endif //_DEBUG
38443895 activate_attribute_context( entry, nullptr, false );
38453896 enter_execution( entry );
38463897
3847- auto onprogress_event = [&]( void ) noexcept
3898+ auto progress_splice = [&]( size_t entry_prev_lex_off ) noexcept
38483899 {
38493900 // If we have a previous calcstack-entry and it the same lexing-point, then we push the current one down there.
38503901 if ( get_calcstack_size() > 1 )
@@ -3851,7 +3902,7 @@
38513902 {
38523903 CalcPathEntry& prev_entry = _calc_stack.GetBack();
38533904
3854- if ( prev_entry.pt.GetOffset() == entry.pt.GetOffset() )
3905+ if ( prev_entry.pt.GetOffset() == entry_prev_lex_off && entry.is_npgated == false )
38553906 {
38563907 #ifdef _DEBUG
38573908 assert( prev_entry.owns_noprogress_active_data == false );
@@ -3858,13 +3909,23 @@
38583909 #endif //_DEBUG
38593910
38603911 prev_entry.noprogress_map = std::move( entry.noprogress_map );
3912+#ifdef _DEBUG
38613913 prev_entry.owns_noprogress_active_data = true;
3914+#endif //_DEBUG
38623915 }
38633916 }
3917+ };
38643918
3919+ auto onprogress_event = [&]( size_t entry_prev_lex_off ) noexcept
3920+ {
3921+ progress_splice( entry_prev_lex_off );
3922+
38653923 // We need entirely new noprogress data.
38663924 entry.noprogress_map.Clear();
3925+#ifdef _DEBUG
38673926 entry.owns_noprogress_active_data = true;
3927+#endif //_DEBUG
3928+ entry.is_npgated = false;
38683929
38693930 // We expect that the user will increase the seek-offset after this function call.
38703931 };
@@ -3886,8 +3947,15 @@
38863947 npdata.edge_use_map.SetBit( edge_idx, used );
38873948 };
38883949
3950+ // Returns true if the next step is known as empty, thus it can be optimized away.
38893951 auto noprogress_is_empty = [&]( Step *next_step ) noexcept
38903952 {
3953+ bool step_is_npgate = next_step->IsNoProgressGate();
3954+
3955+ // npgates cannot be known-empty.
3956+ if ( step_is_npgate )
3957+ return false;
3958+
38913959 // If the node has failed then it for sure is not empty.
38923960 auto *noprogress_data_node = entry.noprogress_map.Find( next_step );
38933961 const noprogress_data *noprogress_data = ( noprogress_data_node ? &noprogress_data_node->GetValue() : nullptr );
@@ -3919,18 +3987,30 @@
39193987
39203988 bool has_op_finished = false;
39213989 bool is_op_successful;
3990+ bool has_substep_exec_been_preempted = false;
39223991
39233992 Step *current_step = start_step;
39243993
39253994 auto push_onto_execution = [&]( Step *next_step, bool force_inline = false )
39263995 {
3996+ bool step_is_npgate = next_step->IsNoProgressGate();
3997+
39273998 // When we enter a node, we say from the get-go that it has made no progress so far.
39283999 // If we have already encountered it then this is NO-OP.
39294000 noprogress_data& npdata = noprogress_enter_node( next_step );
39304001
3931- // If the Step is not empty and not sensibly traversible, then it has to be a denied production, in which case we can avoid going into it.
3932- if ( npdata.is_not_sensibly_traversible == true && npdata.is_empty == false )
3933- return false;
4002+ // No-progress-gates are not short-circuited by their result state.
4003+ if ( step_is_npgate == false )
4004+ {
4005+ // If the Step is not empty and not sensibly traversible, then it has to be a denied production, in which case we can avoid going into it.
4006+ if ( npdata.is_not_sensibly_traversible == true && npdata.is_empty == false )
4007+ {
4008+ has_op_finished = true;
4009+ has_substep_exec_been_preempted = true;
4010+ is_op_successful = false;
4011+ return false;
4012+ }
4013+ }
39344014
39354015 size_t calcstack_cnt = get_calcstack_size();
39364016
@@ -3972,6 +4052,9 @@
39724052 if ( first_found_exec == next_step )
39734053 {
39744054 // We would just endlessly stack so bail.
4055+ has_op_finished = true;
4056+ has_substep_exec_been_preempted = true;
4057+ is_op_successful = false;
39754058 return false;
39764059 }
39774060 }
@@ -4039,12 +4122,15 @@
40394122 // If we did not find a previous invocation then we just start from the beginning.
40404123 }
40414124
4042- // Skip any steps which have already been taken under no-progress.
4043- size_t edge_use_marker_cnt = npdata.edge_use_map.GetBitCount();
4125+ if ( step_is_npgate == false )
4126+ {
4127+ // Skip any steps which have already been taken under no-progress.
4128+ size_t edge_use_marker_cnt = npdata.edge_use_map.GetBitCount();
40444129
4045- while ( skip_stepcnt < edge_use_marker_cnt && npdata.edge_use_map.GetBit( skip_stepcnt ) == true )
4046- {
4047- skip_stepcnt++;
4130+ while ( skip_stepcnt < edge_use_marker_cnt && npdata.edge_use_map.GetBit( skip_stepcnt ) == true )
4131+ {
4132+ skip_stepcnt++;
4133+ }
40484134 }
40494135
40504136 // Make sure that we take into account any break conditions.
@@ -4053,9 +4139,19 @@
40534139 CalcPathEntry new_entry( next_step );
40544140 new_entry.process_idx = skip_stepcnt;
40554141 new_entry.pt = entry.pt;
4056- new_entry.noprogress_map = std::move( entry.noprogress_map );
4142+ if ( step_is_npgate )
4143+ {
4144+ // noprogress_map must be left empty.
4145+ new_entry.is_npgated = true;
4146+ }
4147+ else
4148+ {
4149+ new_entry.noprogress_map = std::move( entry.noprogress_map );
4150+ }
4151+#ifdef _DEBUG
40574152 new_entry.owns_noprogress_active_data = true;
40584153 entry.owns_noprogress_active_data = false;
4154+#endif //_DEBUG
40594155 activate_attribute_context( new_entry, &entry, force_inline );
40604156 enter_execution( new_entry );
40614157
@@ -4066,25 +4162,28 @@
40664162
40674163 current_step = next_step;
40684164
4069- // If we have exhausted all options just by noprogress, then we are empty.
4070- // We succeed and skip anything empty because it works.
4071- // NOTE: we optimize tail-recursions because if the first part is empty, then
4072- // the last step which is not included in the choice count is empty aswell.
4073- if ( noprogress_is_empty( next_step ) )
4165+ if ( step_is_npgate == false )
40744166 {
4075- has_op_finished = true;
4076- is_op_successful = true;
4077- return true;
4078- }
4167+ // If we have exhausted all options just by noprogress, then we are empty.
4168+ // We succeed and skip anything empty because it works.
4169+ // NOTE: we optimize tail-recursions because if the first part is empty, then
4170+ // the last step which is not included in the choice count is empty aswell.
4171+ if ( noprogress_is_empty( next_step ) )
4172+ {
4173+ has_op_finished = true;
4174+ is_op_successful = true;
4175+ return true;
4176+ }
40794177
4080- bool skip_empty_recursion_protection =
4081- ( next_step->disable_empty_recursion_protection || next_step->IsInfiniteEmptyRecursionSecure() );
4178+ bool skip_empty_recursion_protection =
4179+ ( next_step->disable_empty_recursion_protection || next_step->IsInfiniteEmptyRecursionSecure() );
40824180
4083- if ( skip_empty_recursion_protection == false )
4084- {
4085- // Make sure that next entries of this node which have no progress aswell use a further index.
4086- // This counts for alternatives as well as sequences.
4087- noprogress_set_edge_used( npdata, entry.process_idx, true );
4181+ if ( skip_empty_recursion_protection == false )
4182+ {
4183+ // Make sure that next entries of this node which have no progress aswell use a further index.
4184+ // This counts for alternatives as well as sequences.
4185+ noprogress_set_edge_used( npdata, entry.process_idx, true );
4186+ }
40884187 }
40894188
40904189 // We started a new procedure so recognize it.
@@ -4250,9 +4349,13 @@
42504349 {
42514350 has_op_finished = true;
42524351 is_op_successful = true;
4352+
4353+ bool atomic_check_lexical_advancement = false;
42534354 {
42544355 LexingPoint& pt = entry.pt;
42554356
4357+ size_t atomic_previous_lexing_offset;
4358+
42564359 if ( StepToken *tkstep = dynamic_cast <StepToken*> ( current_step ) )
42574360 {
42584361 #ifdef _DEBUG
@@ -4262,10 +4365,14 @@
42624365 // Fixed token processing.
42634366 auto& token_str = tkstep->token_str;
42644367
4368+ atomic_previous_lexing_offset = ( pt.GetOffset() );
4369+
42654370 is_op_successful = pt.HasToken( token_str.ToFixed() );
42664371
42674372 if ( is_op_successful )
42684373 {
4374+ atomic_check_lexical_advancement = true;
4375+
42694376 entry.appendAttributePrefix( current_step->attribute_key.ToFixed() );
42704377 try
42714378 {
@@ -4326,11 +4433,15 @@
43264433 assert( entry.process_idx == 0 );
43274434 #endif //_DEBUG
43284435
4436+ atomic_previous_lexing_offset = ( pt.GetOffset() );
4437+
43294438 // Variable token processing.
43304439 auto result = ptkstep->callback( pt );
43314440
43324441 if ( result.has_value() )
43334442 {
4443+ atomic_check_lexical_advancement = true;
4444+
43344445 auto& restok = *result;
43354446
43364447 is_op_successful = true;
@@ -4420,11 +4531,7 @@
44204531 next_step = actual_call_step;
44214532 }
44224533
4423- if ( push_onto_execution( next_step ) == false )
4424- {
4425- has_op_finished = true;
4426- is_op_successful = false;
4427- }
4534+ push_onto_execution( next_step );
44284535 }
44294536 else if ( StepCall *callstep = dynamic_cast <StepCall*> ( current_step ) )
44304537 {
@@ -4461,11 +4568,7 @@
44614568 next_step = actual_call_step;
44624569 }
44634570
4464- if ( push_onto_execution( next_step, callstep->is_inline ) == false )
4465- {
4466- has_op_finished = true;
4467- is_op_successful = false;
4468- }
4571+ push_onto_execution( next_step, callstep->is_inline );
44694572 }
44704573 }
44714574 else if ( StepRepeatAny *rpstep = dynamic_cast <StepRepeatAny*> ( current_step ) )
@@ -4472,11 +4575,7 @@
44724575 {
44734576 Step *to_be_repeated = rpstep->to_be_repeated;
44744577
4475- if ( push_onto_execution( to_be_repeated ) == false )
4476- {
4477- has_op_finished = true;
4478- is_op_successful = false;
4479- }
4578+ push_onto_execution( to_be_repeated );
44804579 }
44814580 else if ( StepRepeatByInterval *rpintstep = dynamic_cast <StepRepeatByInterval*> ( current_step ) )
44824581 {
@@ -4492,11 +4591,7 @@
44924591 }
44934592 else
44944593 {
4495- if ( push_onto_execution( to_be_repeated ) == false )
4496- {
4497- has_op_finished = true;
4498- is_op_successful = false;
4499- }
4594+ push_onto_execution( to_be_repeated );
45004595 }
45014596 }
45024597 else if ( StepAlternatives *altsteps = dynamic_cast <StepAlternatives*> ( current_step ) )
@@ -4514,12 +4609,7 @@
45144609 {
45154610 Step *next_step = alts[ entry.process_idx ];
45164611
4517- if ( push_onto_execution( next_step ) == false )
4518- {
4519- has_op_finished = false;
4520-
4521- entry.process_idx++;
4522- }
4612+ push_onto_execution( next_step );
45234613 }
45244614 }
45254615 else if ( SerializedStep *serstep = dynamic_cast <SerializedStep*> ( current_step ) )
@@ -4535,11 +4625,7 @@
45354625 {
45364626 Step *next_step = substeps[ entry.process_idx ];
45374627
4538- if ( push_onto_execution( next_step ) == false )
4539- {
4540- has_op_finished = true;
4541- is_op_successful = false;
4542- }
4628+ push_onto_execution( next_step );
45434629 }
45444630 }
45454631 else if ( StepSeparation *sepstep = dynamic_cast <StepSeparation*> ( current_step ) )
@@ -4569,11 +4655,7 @@
45694655 next_step = sepstep->separationStep;
45704656 }
45714657
4572- if ( push_onto_execution( next_step ) == false )
4573- {
4574- has_op_finished = true;
4575- is_op_successful = false;
4576- }
4658+ push_onto_execution( next_step );
45774659 }
45784660 else if ( StepExtend *extstep = dynamic_cast <StepExtend*> ( current_step ) )
45794661 {
@@ -4598,14 +4680,8 @@
45984680 next_step = find_next_specialized_step( next_step, true );
45994681 }
46004682
4601- if ( push_onto_execution( next_step ) == false )
4683+ if ( push_onto_execution( next_step ) == true )
46024684 {
4603- has_op_finished = false;
4604-
4605- entry.process_idx++;
4606- }
4607- else
4608- {
46094685 // Mark the called frame as special.
46104686 // Extension-search should know that the frame is not the most-special one.
46114687 entry.was_called_by_extend = true;
@@ -4615,12 +4691,7 @@
46154691 {
46164692 Step *next_step = extstep->extension;
46174693
4618- if ( push_onto_execution( next_step ) == false )
4619- {
4620- has_op_finished = false;
4621-
4622- entry.process_idx++;
4623- }
4694+ push_onto_execution( next_step );
46244695 }
46254696 }
46264697 }
@@ -4632,11 +4703,7 @@
46324703
46334704 Step *exec = extptstep->exec;
46344705
4635- if ( push_onto_execution( exec ) == false )
4636- {
4637- has_op_finished = true;
4638- is_op_successful = false;
4639- }
4706+ push_onto_execution( exec );
46404707 }
46414708 else if ( StepProceduralAlternatives *procaltstep = dynamic_cast <StepProceduralAlternatives*> ( current_step ) )
46424709 {
@@ -4655,12 +4722,7 @@
46554722 {
46564723 Step *next_step = alts[ entry.process_idx ];
46574724
4658- if ( push_onto_execution( next_step ) == false )
4659- {
4660- has_op_finished = false;
4661-
4662- entry.process_idx++;
4663- }
4725+ push_onto_execution( next_step );
46644726 }
46654727 }
46664728 else if ( StepProceduralSequence *procseqstep = dynamic_cast <StepProceduralSequence*> ( current_step ) )
@@ -4680,11 +4742,7 @@
46804742 {
46814743 Step *next_step = steps[ next_idx ];
46824744
4683- if ( push_onto_execution( next_step ) == false )
4684- {
4685- has_op_finished = true;
4686- is_op_successful = false;
4687- }
4745+ push_onto_execution( next_step );
46884746 }
46894747 }
46904748 else if ( StepCustomIndirection *indirstep = dynamic_cast <StepCustomIndirection*> ( current_step ) )
@@ -4697,10 +4755,9 @@
46974755 has_op_finished = true;
46984756 is_op_successful = true;
46994757 }
4700- else if ( push_onto_execution( next_step ) == false )
4758+ else
47014759 {
4702- has_op_finished = true;
4703- is_op_successful = false;
4760+ push_onto_execution( next_step );
47044761 }
47054762 }
47064763 else if ( StepReplacementPoint *replstep = dynamic_cast <StepReplacementPoint*> ( current_step ) )
@@ -4707,11 +4764,7 @@
47074764 {
47084765 Step *next_step = replstep->exec;
47094766
4710- if ( push_onto_execution( next_step ) == false )
4711- {
4712- has_op_finished = true;
4713- is_op_successful = false;
4714- }
4767+ push_onto_execution( next_step );
47154768 }
47164769 else if ( StepDisabled *denystep = dynamic_cast <StepDisabled*> ( current_step ) )
47174770 {
@@ -4722,251 +4775,300 @@
47224775 {
47234776 FATAL_ABORT();
47244777 }
4778+
4779+ if ( atomic_check_lexical_advancement && is_op_successful )
4780+ {
4781+#ifdef _DEBUG
4782+ assert( has_op_finished == true );
4783+#endif //_DEBUG
4784+
4785+ if ( entry.pt.GetOffset() > atomic_previous_lexing_offset )
4786+ {
4787+ // Since we have made progress using this step, we should allow re-entering it.
4788+ entry.noprogress_map.Remove( current_step );
4789+
4790+ // Update the noprogress information to reflect progress.
4791+ onprogress_event( atomic_previous_lexing_offset );
4792+ }
4793+ }
47254794 }
47264795
47274796 while ( has_op_finished )
47284797 {
4729- this->ExecuteBreakCondition( eBreakCondition::STEP_LEAVE, current_step );
4730-
4731- if ( get_calcstack_size() == 1 )
4798+ if ( has_substep_exec_been_preempted == false )
47324799 {
4733- // The_GTA: I have decided that it is a good idea to have an implicit
4734- // empty at the very last production. This is good for the following reasons:
4735- // 1) this lexing system is meant to parse DETERMINISTIC CFG rules! hence
4736- // if one execution path determines that the input sequence makes sense
4737- // then we pick it. WE EXCLUDE THE RETURN-EMPTY AT THE VERY END FROM
4738- // THE EQUATION! If we empty at end if not present, then it means that
4739- // the deterministic result has been denied. Since our system is deterministic
4740- // it means that only one result is allowed for each input string. Since
4741- // the only input result is denied it means that there is NO RESULT.
4742- // 2) putting the implicit empty at the end is very important to decline results
4743- // which are incomplete. It could be that rules have succeeded to understand
4744- // a partial result BUT IN THAT CASE WE DO NO CARE ABOUT HALF-ASSEDNESS! Thus
4745- // we reject the incomplete result.
4800+ this->ExecuteBreakCondition( eBreakCondition::STEP_LEAVE, current_step );
47464801
4747- bool implicit_end_success = ( entry.pt.GetRemainder() == 0 );
4748-
4749- // Delete any previous node.
4750- if ( cNodeType *prevResNode = this->resultNode )
4802+ if ( get_calcstack_size() == 1 )
47514803 {
4752- prevResNode->Delete();
4753- }
4804+ // The_GTA: I have decided that it is a good idea to have an implicit
4805+ // empty at the very last production. This is good for the following reasons:
4806+ // 1) this lexing system is meant to parse DETERMINISTIC CFG rules! hence
4807+ // if one execution path determines that the input sequence makes sense
4808+ // then we pick it. WE EXCLUDE THE RETURN-EMPTY AT THE VERY END FROM
4809+ // THE EQUATION! If we empty at end if not present, then it means that
4810+ // the deterministic result has been denied. Since our system is deterministic
4811+ // it means that only one result is allowed for each input string. Since
4812+ // the only input result is denied it means that there is NO RESULT.
4813+ // 2) putting the implicit empty at the end is very important to decline results
4814+ // which are incomplete. It could be that rules have succeeded to understand
4815+ // a partial result BUT IN THAT CASE WE DO NO CARE ABOUT HALF-ASSEDNESS! Thus
4816+ // we reject the incomplete result.
47544817
4755- if ( implicit_end_success == true )
4756- {
4757- catchup_stack_init();
4818+ bool implicit_end_success = ( entry.pt.GetRemainder() == 0 );
47584819
4759- this->successful = is_op_successful;
4760- this->resultNode = entry.DetachNode();
4761- }
4762- else
4763- {
4764- this->successful = false;
4765- this->resultNode = nullptr;
4766- }
4820+ // Delete any previous node.
4821+ if ( cNodeType *prevResNode = this->resultNode )
4822+ {
4823+ prevResNode->Delete();
4824+ }
47674825
4768- // Need to leave the top-most item.
4769- leave_execution( entry );
4826+ if ( implicit_end_success == true )
4827+ {
4828+ catchup_stack_init();
47704829
4830+ this->successful = is_op_successful;
4831+ this->resultNode = entry.DetachNode();
4832+ }
4833+ else
4834+ {
4835+ this->successful = false;
4836+ this->resultNode = nullptr;
4837+ }
4838+
4839+ // Need to leave the top-most item.
4840+ leave_execution( entry );
4841+
47714842 #ifdef _DEBUG
4772- if constexpr ( std::derived_from <cNodeType, Step> )
4773- {
4774- // ???
4775- if ( is_op_successful && this->resultNode == nullptr )
4843+ if constexpr ( std::derived_from <cNodeType, Step> )
47764844 {
4777- __debugbreak();
4845+ // ???
4846+ if ( is_op_successful && this->resultNode == nullptr )
4847+ {
4848+ __debugbreak();
4849+ }
47784850 }
4851+#endif //_DEBUG
4852+ break;
47794853 }
4780-#endif //_DEBUG
4781- break;
4782- }
47834854
4784- // Pop the top-most item from the calc-stack.
4785- CalcPathEntry prev_entry = std::move( entry );
4855+ // Pop the top-most item from the calc-stack.
4856+ CalcPathEntry prev_entry = std::move( entry );
47864857
4787- entry = std::move( _calc_stack.GetBack() );
4788- _calc_stack.RemoveFromBack();
4858+ entry = std::move( _calc_stack.GetBack() );
4859+ _calc_stack.RemoveFromBack();
47894860
4790- if ( entry.pt.GetOffset() == prev_entry.pt.GetOffset() )
4791- {
4792- // We have to take-back the noprogress_map with no-recursion information.
4793- entry.noprogress_map = std::move( prev_entry.noprogress_map );
4794- entry.owns_noprogress_active_data = true;
4795- }
4796- else
4797- {
47984861 #ifdef _DEBUG
4799- assert( prev_entry.owns_noprogress_active_data == true );
4862+ if ( entry.pt.GetOffset() != prev_entry.pt.GetOffset() )
4863+ {
4864+ assert( prev_entry.owns_noprogress_active_data == true );
4865+ }
48004866 #endif //_DEBUG
4801- }
48024867
4803- try
4804- {
4805- // Since we have returned from a call, increment our process index.
4806- entry.process_idx++;
4868+ try
4869+ {
4870+ bool take_over_progress = false;
48074871
4808- bool token_made_progress = false;
4872+ // Decide whether we should take-over the noprogress details.
4873+ bool lexical_advancement = ( prev_entry.pt.GetOffset() > entry.pt.GetOffset() );
48094874
4810- // Execute the noprogress logic.
4811- if ( is_op_successful )
4812- {
4813- // Have we made progress?
4814- if ( prev_entry.pt.GetOffset() > entry.pt.GetOffset() )
4875+ if ( is_op_successful || lexical_advancement == false )
48154876 {
4816- token_made_progress = true;
4877+ // TODO: what to do about returning from a npgate?
48174878
4818- // The current step should be removed from the noprogress map because it did make successful progress
4819- // after all.
4820- prev_entry.noprogress_map.Remove( current_step );
4879+ // We have to take the noprogress-data if we made progress so we can push it further
4880+ // down or clear it completely.
4881+ // If we made no progress then we will simply keep the data.
48214882
4822- entry.noprogress_map = std::move( prev_entry.noprogress_map );
4823- entry.owns_noprogress_active_data = true;
4883+ bool ignore_prev_npgate = false;
48244884
4825- onprogress_event();
4885+ if ( is_op_successful && lexical_advancement )
4886+ {
4887+ // The current step should be removed from the noprogress map because it did make successful progress
4888+ // after all.
4889+ entry.noprogress_map.Remove( current_step );
4890+
4891+ progress_splice( entry.pt.GetOffset() );
4892+
4893+ entry.is_npgated = false;
4894+
4895+ ignore_prev_npgate = true;
4896+ }
4897+
4898+ if ( ignore_prev_npgate || prev_entry.is_npgated == false )
4899+ {
4900+ entry.noprogress_map = std::move( prev_entry.noprogress_map );
4901+#ifdef _DEBUG
4902+ entry.owns_noprogress_active_data = true;
4903+#endif //_DEBUG
4904+ }
48264905 }
4906+
4907+ // Execute the noprogress logic.
4908+ if ( is_op_successful )
4909+ {
4910+ // Have we made progress?
4911+ if ( lexical_advancement )
4912+ {
4913+ take_over_progress = true;
4914+ }
4915+ else
4916+ {
4917+ noprogress_data& npdata = entry.noprogress_map[ current_step ];
4918+
4919+ // We are empty.
4920+ npdata.is_empty = true;
4921+ npdata.is_not_sensibly_traversible = true;
4922+ }
4923+ }
48274924 else
48284925 {
48294926 noprogress_data& npdata = entry.noprogress_map[ current_step ];
48304927
4831- // We are empty.
4832- npdata.is_empty = true;
4928+ // Do we move back the seek offset?
4929+ // Then we just ignore the data that we collected under entry.
4930+
4931+ // We are not sensibly traversible but are not empty (denied > empty).
4932+ npdata.is_empty = false;
48334933 npdata.is_not_sensibly_traversible = true;
48344934 }
4835- }
4836- else
4837- {
4838- noprogress_data& npdata = entry.noprogress_map[ current_step ];
4839-
4840- // Do we move back the seek offset?
4841- // Then we just ignore the data that we collected under entry.
4842-
4843- // We are not sensibly traversible but are not empty (denied > empty).
4844- npdata.is_empty = false;
4845- npdata.is_not_sensibly_traversible = true;
4846- }
48474935
4848- Step *prev_step = current_step;
4936+ Step *prev_step = current_step;
48494937
4850- current_step = entry.current_step;
4938+ current_step = entry.current_step;
48514939
4852- // If we were successful, then we should advance the lexing point by taking the
4853- // one from the previous entry. Otherwise we have to try again and stuff.
4854- if ( token_made_progress )
4855- {
4856- entry.pt.SetOffset( prev_entry.pt.GetOffset() );
4857- }
4940+ // If we were successful, then we should advance the lexing point by taking the
4941+ // one from the previous entry. Otherwise we have to try again and stuff.
4942+ if ( take_over_progress )
4943+ {
4944+ entry.pt.SetOffset( prev_entry.pt.GetOffset() );
4945+ }
48584946
4859- // Execute on-return logic.
4860- // This is important because we need the return value which is present in the previous frame.
4861- if ( is_op_successful )
4862- {
4863- bool has_performed_graph_mutation = false;
4947+ // Execute on-return logic.
4948+ // This is important because we need the return value which is present in the previous frame.
4949+ if ( is_op_successful )
4950+ {
4951+ bool has_performed_graph_mutation = false;
48644952
4865- if ( prev_entry.is_inline_call )
4866- {
4867- // If we are not initialized, then no change was performed.
4868- // If we are SSO, then we do not have to take the value back because we have exactly
4869- // the same one down the line.
4870- if ( prev_entry.requires_init == false && prev_entry.is_sso == false )
4953+ if ( prev_entry.is_inline_call )
48714954 {
4872- // Take-over the calculatory state completely.
4873- if ( std::holds_alternative <cNodeType*> ( prev_entry.pcalc ) )
4955+ // If we are not initialized, then no change was performed.
4956+ // If we are SSO, then we do not have to take the value back because we have exactly
4957+ // the same one down the line.
4958+ if ( prev_entry.requires_init == false && prev_entry.is_sso == false )
48744959 {
4875- cNodeType *resNode = std::get <cNodeType*> ( prev_entry.pcalc );
4960+ // Take-over the calculatory state completely.
4961+ if ( std::holds_alternative <cNodeType*> ( prev_entry.pcalc ) )
4962+ {
4963+ cNodeType *resNode = std::get <cNodeType*> ( prev_entry.pcalc );
48764964
4877- prev_entry.pcalc = std::monostate();
4878- prev_entry.is_sso = false;
4965+ prev_entry.pcalc = std::monostate();
4966+ prev_entry.is_sso = false;
48794967
4880- bool has_assigned = entry.ReplaceNode( resNode );
4968+ bool has_assigned = entry.ReplaceNode( resNode );
48814969
4882- if ( has_assigned == false && resNode != nullptr )
4970+ if ( has_assigned == false && resNode != nullptr )
4971+ {
4972+ // Apparently we have the same node twice in the system but we have
4973+ // previously assumed it is a different node, thus we have to remove the
4974+ // unnecessary double reference.
4975+ this->env->lifetimeMan.RemRef( resNode );
4976+ }
4977+
4978+ // This graph mutation can be neglected.
4979+ }
4980+ else if ( std::holds_alternative <node_selector*> ( prev_entry.pcalc ) )
48834981 {
4884- // Apparently we have the same node twice in the system but we have
4885- // previously assumed it is a different node, thus we have to remove the
4886- // unnecessary double reference.
4887- this->env->lifetimeMan.RemRef( resNode );
4888- }
4982+ node_selector *sel_man = std::get <node_selector*> ( prev_entry.pcalc );
48894983
4890- // This graph mutation can be neglected.
4891- }
4892- else if ( std::holds_alternative <node_selector*> ( prev_entry.pcalc ) )
4893- {
4894- node_selector *sel_man = std::get <node_selector*> ( prev_entry.pcalc );
4984+ // The selector has to be the same as from the caller.
48954985
4896- // The selector has to be the same as from the caller.
4986+ if ( void *newMetaData = prev_entry.selectorMetaData )
4987+ {
4988+ void *oldMetaData = entry.selectorMetaData;
48974989
4898- if ( void *newMetaData = prev_entry.selectorMetaData )
4899- {
4900- void *oldMetaData = entry.selectorMetaData;
4901-
49024990 #ifdef _DEBUG
4903- assert( prev_entry.selectorMetaData_initialized == true );
4904- assert( oldMetaData != nullptr );
4991+ assert( prev_entry.selectorMetaData_initialized == true );
4992+ assert( oldMetaData != nullptr );
49054993 #endif //_DEBUG
49064994
4907- sel_man->DestroyMetaData( oldMetaData );
4908- sel_man->MoveConstructMetaData( oldMetaData, newMetaData );
4995+ sel_man->DestroyMetaData( oldMetaData );
4996+ sel_man->MoveConstructMetaData( oldMetaData, newMetaData );
4997+ }
4998+ // else we have no data to take over anyway.
4999+
5000+ // We don't know what nodes could have been referenced or dereferenced so just
5001+ // assume that we mutated the graph.
5002+ has_performed_graph_mutation = true;
49095003 }
4910- // else we have no data to take over anyway.
4911-
4912- // We don't know what nodes could have been referenced or dereferenced so just
4913- // assume that we mutated the graph.
4914- has_performed_graph_mutation = true;
5004+#ifdef _DEBUG
5005+ else if ( std::holds_alternative <std::monostate> ( prev_entry.pcalc ) == false )
5006+ {
5007+ // Unknown inline return logic.
5008+ assert( 0 );
5009+ }
5010+#endif //_DEBUG
49155011 }
4916-#ifdef _DEBUG
4917- else if ( std::holds_alternative <std::monostate> ( prev_entry.pcalc ) == false )
5012+ else
49185013 {
4919- // Unknown inline return logic.
4920- assert( 0 );
5014+ // Since nothing has been done we can just skip taking a node with no change.
5015+ // This is an optimization.
49215016 }
4922-#endif //_DEBUG
49235017 }
49245018 else
49255019 {
4926- // Since nothing has been done we can just skip taking a node with no change.
4927- // This is an optimization.
4928- }
4929- }
4930- else
4931- {
4932- // Take over just the node, if available.
4933- cNodeType *resNode;
5020+ // Take over just the node, if available.
5021+ cNodeType *resNode;
49345022
4935- if ( prev_entry.requires_init )
4936- {
4937- node_dispatcher *node_dispatch = prev_step->node_dispatch;
4938-
4939- if ( node_constructor *node_constr = dynamic_cast <node_constructor*> ( node_dispatch ) )
5023+ if ( prev_entry.requires_init )
49405024 {
4941- resNode = node_constr->CreateNode();
4942- }
4943- else if ( node_selector *sel_man = dynamic_cast <node_selector*> ( node_dispatch ) )
4944- {
4945- // This is a weird thing but we have to temporarily create the selector in order to obtain its node
4946- // and then delete it again.
4947- size_t dataSize, align;
4948- sel_man->GetMetaDataProperties( dataSize, align );
5025+ node_dispatcher *node_dispatch = prev_step->node_dispatch;
49495026
4950- void *selMetaData = nullptr;
4951-
4952- try
5027+ if ( node_constructor *node_constr = dynamic_cast <node_constructor*> ( node_dispatch ) )
49535028 {
4954- if ( dataSize > 0 )
4955- {
4956- selMetaData = stacked_alloc.Allocate( dataSize, align );
5029+ resNode = node_constr->CreateNode();
5030+ }
5031+ else if ( node_selector *sel_man = dynamic_cast <node_selector*> ( node_dispatch ) )
5032+ {
5033+ // This is a weird thing but we have to temporarily create the selector in order to obtain its node
5034+ // and then delete it again.
5035+ size_t dataSize, align;
5036+ sel_man->GetMetaDataProperties( dataSize, align );
49575037
4958- sel_man->ConstructMetaData( selMetaData );
4959- }
5038+ void *selMetaData = nullptr;
49605039
49615040 try
49625041 {
4963- resNode = sel_man->DetachFinishedNode( selMetaData );
5042+ if ( dataSize > 0 )
5043+ {
5044+ selMetaData = stacked_alloc.Allocate( dataSize, align );
5045+
5046+ sel_man->ConstructMetaData( selMetaData );
5047+ }
5048+
5049+ try
5050+ {
5051+ resNode = sel_man->DetachFinishedNode( selMetaData );
5052+ }
5053+ catch( ... )
5054+ {
5055+ if ( selMetaData )
5056+ {
5057+ sel_man->DestroyMetaData( selMetaData );
5058+ }
5059+ throw;
5060+ }
5061+
5062+ if ( selMetaData )
5063+ {
5064+ sel_man->DestroyMetaData( selMetaData );
5065+ }
49645066 }
49655067 catch( ... )
49665068 {
49675069 if ( selMetaData )
49685070 {
4969- sel_man->DestroyMetaData( selMetaData );
5071+ stacked_alloc.Deallocate( selMetaData );
49705072 }
49715073 throw;
49725074 }
@@ -4973,300 +5075,297 @@
49735075
49745076 if ( selMetaData )
49755077 {
4976- sel_man->DestroyMetaData( selMetaData );
5078+ stacked_alloc.Deallocate( selMetaData );
49775079 }
49785080 }
4979- catch( ... )
5081+ else
49805082 {
4981- if ( selMetaData )
4982- {
4983- stacked_alloc.Deallocate( selMetaData );
4984- }
4985- throw;
4986- }
5083+#ifdef _DEBUG
5084+ assert( 0 );
5085+#endif //_DEBUG
49875086
4988- if ( selMetaData )
4989- {
4990- stacked_alloc.Deallocate( selMetaData );
5087+ // Unknown logic.
5088+ resNode = nullptr;
49915089 }
49925090 }
49935091 else
49945092 {
4995-#ifdef _DEBUG
4996- assert( 0 );
4997-#endif //_DEBUG
4998-
4999- // Unknown logic.
5000- resNode = nullptr;
5093+ // Take-over a regular and customized return node.
5094+ resNode = prev_entry.DetachNode();
50015095 }
5002- }
5003- else
5004- {
5005- // Take-over a regular and customized return node.
5006- resNode = prev_entry.DetachNode();
5007- }
50085096
5009- // If we have a return value that should be written to an attribute location, do so.
5010- if ( resNode )
5011- {
5012- // Any node that we have taken has been dereferenced here.
5013- // Thus we have right now mutated the graph in such a way that resNode (could) be removed from the system.
5014- // If we have a new node then it is a good idea to cache all the relationships thus even if we do not
5015- // garbage-collect any new nodes we do important progress in graph visitation.
5016- has_performed_graph_mutation = true;
5017-
5018- try
5097+ // If we have a return value that should be written to an attribute location, do so.
5098+ if ( resNode )
50195099 {
5020- catchup_stack_init();
5100+ // Any node that we have taken has been dereferenced here.
5101+ // Thus we have right now mutated the graph in such a way that resNode (could) be removed from the system.
5102+ // If we have a new node then it is a good idea to cache all the relationships thus even if we do not
5103+ // garbage-collect any new nodes we do important progress in graph visitation.
5104+ has_performed_graph_mutation = true;
50215105
5022- bool has_initialized_attrib_prefix = false;
5023- //bool initialized_attrib_is_prev_already_capsulation;
5106+ try
50245107 {
5025- // Need to possibly also devolve down the calculation path.
5026- CalcPathDevolutionIterator dev_iter( _calc_stack, entry );
5108+ catchup_stack_init();
50275109
5028- tryNextNode3:
5029- CalcPathEntry& assign_node_entry = dev_iter.GetCurrentEntry();
5110+ bool has_initialized_attrib_prefix = false;
5111+ //bool initialized_attrib_is_prev_already_capsulation;
5112+ {
5113+ // Need to possibly also devolve down the calculation path.
5114+ CalcPathDevolutionIterator dev_iter( _calc_stack, entry );
50305115
5031- // Make sure we initialize to this point.
5032- calcstack_init_to_entry( assign_node_entry );
5116+ tryNextNode3:
5117+ CalcPathEntry& assign_node_entry = dev_iter.GetCurrentEntry();
50335118
5034- bool has_node = false;
5035- node_selector *sel_man = nullptr;
5119+ // Make sure we initialize to this point.
5120+ calcstack_init_to_entry( assign_node_entry );
50365121
5037- if constexpr ( LexerStructHasAssignNode <cNodeType, charType, cNodeType> )
5038- {
5039- has_node = std::holds_alternative <cNodeType*> ( assign_node_entry.pcalc );
5040- }
5122+ bool has_node = false;
5123+ node_selector *sel_man = nullptr;
50415124
5042- if ( has_node == false )
5043- {
5044- sel_man = assign_node_entry.GetActiveSelector();
5045- }
5125+ if constexpr ( LexerStructHasAssignNode <cNodeType, charType, cNodeType> )
5126+ {
5127+ has_node = std::holds_alternative <cNodeType*> ( assign_node_entry.pcalc );
5128+ }
50465129
5047- bool has_selector = ( sel_man != nullptr );
5048-
5049- if ( has_node || has_selector )
5050- {
5051- if ( !has_initialized_attrib_prefix )
5130+ if ( has_node == false )
50525131 {
5053- entry.appendAttributePrefix( current_step->attribute_key.ToFixed() );
5054- //entry.appendAttributePrefix( prev_step->attribute_key.ToFixed() );
5055-
5056- has_initialized_attrib_prefix = true;
5132+ sel_man = assign_node_entry.GetActiveSelector();
50575133 }
50585134
5059- try
5135+ bool has_selector = ( sel_man != nullptr );
5136+
5137+ if ( has_node || has_selector )
50605138 {
5061- const auto& attrib_name = dev_iter.GetAttributePrefix();
5139+ if ( !has_initialized_attrib_prefix )
5140+ {
5141+ entry.appendAttributePrefix( current_step->attribute_key.ToFixed() );
5142+ //entry.appendAttributePrefix( prev_step->attribute_key.ToFixed() );
50625143
5063- bool couldAssign;
5144+ has_initialized_attrib_prefix = true;
5145+ }
50645146
5065- if ( has_node )
5147+ try
50665148 {
5067- if constexpr ( LexerStructHasAssignNode <cNodeType, charType, cNodeType> )
5149+ const auto& attrib_name = dev_iter.GetAttributePrefix();
5150+
5151+ bool couldAssign;
5152+
5153+ if ( has_node )
50685154 {
5069- cNodeType *set_node = std::get <cNodeType*> ( assign_node_entry.pcalc );
5155+ if constexpr ( LexerStructHasAssignNode <cNodeType, charType, cNodeType> )
5156+ {
5157+ cNodeType *set_node = std::get <cNodeType*> ( assign_node_entry.pcalc );
50705158
5071- couldAssign = set_node->AssignNode( attrib_name.ToFixed(), resNode );
5159+ couldAssign = set_node->AssignNode( attrib_name.ToFixed(), resNode );
5160+ }
5161+ else
5162+ {
5163+ couldAssign = false;
5164+ }
50725165 }
50735166 else
50745167 {
5075- couldAssign = false;
5168+ couldAssign = sel_man->AssignNode( assign_node_entry.selectorMetaData, attrib_name.ToFixed(), resNode );
50765169 }
5170+
5171+ if ( couldAssign )
5172+ {
5173+ // Since the node was successfully given into another node, it does not have to be
5174+ // added a reference count to.
5175+
5176+ resNode = nullptr;
5177+ }
50775178 }
5078- else
5179+ catch( lexing_invalid_parameter_exception& )
50795180 {
5080- couldAssign = sel_man->AssignNode( assign_node_entry.selectorMetaData, attrib_name.ToFixed(), resNode );
5181+ // We have actually failed.
5182+ is_op_successful = false;
50815183 }
5082-
5083- if ( couldAssign )
5184+ catch( lexing_value_transgression_exception& )
50845185 {
5085- // Since the node was successfully given into another node, it does not have to be
5086- // added a reference count to.
5186+ // Have to try the next node, if not at the end of course.
5187+ dev_iter.Increment();
50875188
5088- resNode = nullptr;
5189+ if ( dev_iter.IsEnd() == false )
5190+ {
5191+ goto tryNextNode3;
5192+ }
50895193 }
50905194 }
5091- catch( lexing_invalid_parameter_exception& )
5195+ // If we are successful and there is no node in the previous step and the previous
5196+ // step is not ambiguous, then write the node as result.
5197+ else if ( current_step->IsUnambiguousCall() )
50925198 {
5093- // We have actually failed.
5094- is_op_successful = false;
5095- }
5096- catch( lexing_value_transgression_exception& )
5097- {
5098- // Have to try the next node, if not at the end of course.
5099- dev_iter.Increment();
5199+ bool has_assigned = assign_node_entry.ReplaceNode( resNode );
51005200
5101- if ( dev_iter.IsEnd() == false )
5201+ if ( has_assigned && resNode != nullptr )
51025202 {
5103- goto tryNextNode3;
5203+ // The node is now safe, so add a ref count to it.
5204+ this->env->lifetimeMan.AddRef( resNode );
51045205 }
5206+
5207+ resNode = nullptr;
51055208 }
51065209 }
5107- // If we are successful and there is no node in the previous step and the previous
5108- // step is not ambiguous, then write the node as result.
5109- else if ( current_step->IsUnambiguousCall() )
5210+ // Now the iterator is destroyed and the calculation stack is in default and proper state.
5211+
5212+ if ( has_initialized_attrib_prefix )
51105213 {
5111- bool has_assigned = assign_node_entry.ReplaceNode( resNode );
5112-
5113- if ( has_assigned && resNode != nullptr )
5114- {
5115- // The node is now safe, so add a ref count to it.
5116- this->env->lifetimeMan.AddRef( resNode );
5117- }
5118-
5119- resNode = nullptr;
5214+ //entry.debuildAttributePrefix( prev_step->attribute_key.ToFixed() );
5215+ entry.debuildAttributePrefix( current_step->attribute_key.ToFixed() );
51205216 }
51215217 }
5122- // Now the iterator is destroyed and the calculation stack is in default and proper state.
5123-
5124- if ( has_initialized_attrib_prefix )
5218+ catch( ... )
51255219 {
5126- //entry.debuildAttributePrefix( prev_step->attribute_key.ToFixed() );
5127- entry.debuildAttributePrefix( current_step->attribute_key.ToFixed() );
5220+ if constexpr ( LexerStructMutable <cNodeType, charType, cNodeType> )
5221+ {
5222+ resNode->Delete();
5223+ }
5224+ throw;
51285225 }
5129- }
5130- catch( ... )
5131- {
5226+
51325227 if constexpr ( LexerStructMutable <cNodeType, charType, cNodeType> )
51335228 {
5134- resNode->Delete();
5229+ if ( resNode )
5230+ {
5231+ resNode->Delete();
5232+ }
51355233 }
5136- throw;
51375234 }
5235+ }
51385236
5139- if constexpr ( LexerStructMutable <cNodeType, charType, cNodeType> )
5140- {
5141- if ( resNode )
5142- {
5143- resNode->Delete();
5144- }
5145- }
5237+ if ( has_performed_graph_mutation )
5238+ {
5239+ // Run the garbage collector because we (might) have changed the graph.
5240+ // If the construct-node-type is not mutable, then the runtime has the chance to clean
5241+ // things up by this function call.
5242+ this->env->lifetimeMan.RunGC();
51465243 }
51475244 }
5148-
5149- if ( has_performed_graph_mutation )
5150- {
5151- // Run the garbage collector because we (might) have changed the graph.
5152- // If the construct-node-type is not mutable, then the runtime has the chance to clean
5153- // things up by this function call.
5154- this->env->lifetimeMan.RunGC();
5155- }
51565245 }
5246+ catch( ... )
5247+ {
5248+ leave_execution( prev_entry );
5249+ throw;
5250+ }
51575251
5158- if ( StepAlternatives *altsteps = dynamic_cast <StepAlternatives*> ( current_step ) )
5252+ // Terminate the entry.
5253+ leave_execution( prev_entry );
5254+
5255+ // Take over any building values.
5256+ deactivate_attribute_context( prev_entry, &entry );
5257+ }
5258+ else
5259+ {
5260+ // Finished handling pre-empted step.
5261+ has_substep_exec_been_preempted = false;
5262+ }
5263+
5264+ // Since we have returned from a call, increment our process index.
5265+ entry.process_idx++;
5266+
5267+ // Handle the success status based on the returned-to step logic.
5268+ // In this switch we HAVE TO overwrite is_op_successful and/or has_op_finished!
5269+ if ( StepAlternatives *altsteps = dynamic_cast <StepAlternatives*> ( current_step ) )
5270+ {
5271+ has_op_finished = ( is_op_successful == true );
5272+ }
5273+ else if ( SerializedStep *sersteps = dynamic_cast <SerializedStep*> ( current_step ) )
5274+ {
5275+ has_op_finished = ( is_op_successful == false );
5276+ }
5277+ else if ( StepRepeatAny *rpstep = dynamic_cast <StepRepeatAny*> ( current_step ) )
5278+ {
5279+ if ( noprogress_is_empty( rpstep->to_be_repeated ) )
51595280 {
5160- has_op_finished = ( is_op_successful == true );
5281+ // Terminate the infinite recursion.
5282+ has_op_finished = true;
5283+ is_op_successful = true;
51615284 }
5162- else if ( SerializedStep *sersteps = dynamic_cast <SerializedStep*> ( current_step ) )
5285+ else
51635286 {
51645287 has_op_finished = ( is_op_successful == false );
5165- }
5166- else if ( StepRepeatAny *rpstep = dynamic_cast <StepRepeatAny*> ( current_step ) )
5167- {
5168- if ( noprogress_is_empty( rpstep->to_be_repeated ) )
5288+
5289+ if ( has_op_finished )
51695290 {
5170- // Terminate the infinite recursion.
5171- has_op_finished = true;
51725291 is_op_successful = true;
51735292 }
5174- else
5175- {
5176- has_op_finished = ( is_op_successful == false );
5293+ }
5294+ }
5295+ else if ( StepRepeatByInterval *rpintstep = dynamic_cast <StepRepeatByInterval*> ( current_step ) )
5296+ {
5297+ bool is_infinite_recursion = noprogress_is_empty( rpintstep->to_be_repeated );
51775298
5178- if ( has_op_finished )
5179- {
5180- is_op_successful = true;
5181- }
5182- }
5183- }
5184- else if ( StepRepeatByInterval *rpintstep = dynamic_cast <StepRepeatByInterval*> ( current_step ) )
5299+ has_op_finished = ( is_infinite_recursion || is_op_successful == false );
5300+
5301+ if ( has_op_finished )
51855302 {
5186- bool is_infinite_recursion = noprogress_is_empty( rpintstep->to_be_repeated );
5303+ size_t actual_repcnt = ( entry.process_idx - 1 );
51875304
5188- has_op_finished = ( is_infinite_recursion || is_op_successful == false );
5189-
5190- if ( has_op_finished )
5305+ if ( rpintstep->has_maximum == false || actual_repcnt <= rpintstep->max_rep_cnt )
51915306 {
5192- size_t actual_repcnt = ( entry.process_idx - 1 );
5193-
5194- if ( rpintstep->has_maximum == false || actual_repcnt <= rpintstep->max_rep_cnt )
5195- {
5196- is_op_successful = ( is_infinite_recursion == true ) || ( actual_repcnt >= rpintstep->min_rep_cnt );
5197- }
5307+ is_op_successful = ( is_infinite_recursion == true ) || ( actual_repcnt >= rpintstep->min_rep_cnt );
51985308 }
51995309 }
5200- else if ( StepSeparation *sepstep = dynamic_cast <StepSeparation*> ( current_step ) )
5201- {
5202- bool is_infinite_recursion = noprogress_is_empty( sepstep->itemStep ) && noprogress_is_empty( sepstep->separationStep );
5310+ }
5311+ else if ( StepSeparation *sepstep = dynamic_cast <StepSeparation*> ( current_step ) )
5312+ {
5313+ bool is_infinite_recursion = noprogress_is_empty( sepstep->itemStep ) && noprogress_is_empty( sepstep->separationStep );
52035314
5204- has_op_finished = ( is_infinite_recursion || is_op_successful == false );
5315+ has_op_finished = ( is_infinite_recursion || is_op_successful == false );
52055316
5206- if ( has_op_finished )
5207- {
5208- size_t actual_proc_cnt = ( entry.process_idx - 1 );
5317+ if ( has_op_finished )
5318+ {
5319+ size_t actual_proc_cnt = ( entry.process_idx - 1 );
52095320
5210- size_t choice_idx = ( actual_proc_cnt % 2 );
5211- size_t item_cnt = ( ( actual_proc_cnt + 1 ) / 2 );
5321+ size_t choice_idx = ( actual_proc_cnt % 2 );
5322+ size_t item_cnt = ( ( actual_proc_cnt + 1 ) / 2 );
52125323
5213- is_op_successful = ( is_infinite_recursion == true ) || ( item_cnt >= sepstep->min_item_cnt );
5324+ is_op_successful = ( is_infinite_recursion == true ) || ( item_cnt >= sepstep->min_item_cnt );
52145325
5215- // By setting is_alternation to true, the separation turns into an alternation of
5216- // item and separator. This means that we do not force-reset to the end of the last successful
5217- // item on fail.
5218- // If we had a separator, we failed on the item, we are NOT allowed to and we succeed the production,
5219- // then we reset to the beginning of the separator.
5220- bool is_alternation = sepstep->is_alternation;
5326+ // By setting is_alternation to true, the separation turns into an alternation of
5327+ // item and separator. This means that we do not force-reset to the end of the last successful
5328+ // item on fail.
5329+ // If we had a separator, we failed on the item, we are NOT allowed to and we succeed the production,
5330+ // then we reset to the beginning of the separator.
5331+ bool is_alternation = sepstep->is_alternation;
52215332
5222- if ( is_op_successful == true && is_alternation == false && choice_idx == 0 && actual_proc_cnt > 0 )
5223- {
5224- auto *metaData = (typename StepSeparation::metaData*)entry.metaData;
5333+ if ( is_op_successful == true && is_alternation == false && choice_idx == 0 && actual_proc_cnt > 0 )
5334+ {
5335+ auto *metaData = (typename StepSeparation::metaData*)entry.metaData;
52255336
52265337 #ifdef _DEBUG
5227- assert( metaData != nullptr );
5338+ assert( metaData != nullptr );
52285339 #endif //_DEBUG
52295340
5230- entry.pt.SetOffset( metaData->resetOff_forItemFail );
5231- }
5341+ entry.pt.SetOffset( metaData->resetOff_forItemFail );
52325342 }
52335343 }
5234- else if ( StepExtend *extstep = dynamic_cast <StepExtend*> ( current_step ) )
5235- {
5236- // Don't forget that we are just a special form of StepAlternatives.
5237- // The reason is purely rooted in modelling freedom as well as performance.
5238- has_op_finished = ( is_op_successful == true );
5239- }
5240- else if ( StepProceduralAlternatives *procaltstep = dynamic_cast <StepProceduralAlternatives*> ( current_step ) )
5241- {
5242- // Just a special form of StepAlternatives.
5243- has_op_finished = ( is_op_successful == true );
5244- }
5245- else
5246- {
5247- // StepExtensionPoint
5248- // StepCall
5249- // StepAttribution
5250- // StepProceduralSequence
5251- // StepCustomIndirection
5252- // StepReplacementPoint
5253- // StepDisabled (moot)
5254-
5255- // This opcode has no further special handling.
5256- has_op_finished = true;
5257- }
52585344 }
5259- catch( ... )
5345+ else if ( StepExtend *extstep = dynamic_cast <StepExtend*> ( current_step ) )
52605346 {
5261- leave_execution( prev_entry );
5262- throw;
5347+ // Don't forget that we are just a special form of StepAlternatives.
5348+ // The reason is purely rooted in modelling freedom as well as performance.
5349+ has_op_finished = ( is_op_successful == true );
52635350 }
5351+ else if ( StepProceduralAlternatives *procaltstep = dynamic_cast <StepProceduralAlternatives*> ( current_step ) )
5352+ {
5353+ // Just a special form of StepAlternatives.
5354+ has_op_finished = ( is_op_successful == true );
5355+ }
5356+ else
5357+ {
5358+ // StepExtensionPoint
5359+ // StepCall
5360+ // StepAttribution
5361+ // StepProceduralSequence
5362+ // StepCustomIndirection
5363+ // StepReplacementPoint
5364+ // StepDisabled (moot)
52645365
5265- // Terminate the entry.
5266- leave_execution( prev_entry );
5267-
5268- // Take over any building values.
5269- deactivate_attribute_context( prev_entry, &entry );
5366+ // This opcode has no further special handling.
5367+ has_op_finished = true;
5368+ }
52705369 }
52715370 }
52725371 }
--- testcompiler/src/cos_tests.cpp (revision 69)
+++ testcompiler/src/cos_tests.cpp (revision 70)
@@ -469,7 +469,7 @@
469469 {
470470 // Just a pack of declarations because we cannot keep them outside.
471471
472- inline MultiDeclarationStatement( void ) noexcept
472+ inline MultiDeclarationStatement( depVector <DeclarationStatement*> decls = {} ) noexcept : decls( std::move( decls ) )
473473 {}
474474 inline MultiDeclarationStatement( const MultiDeclarationStatement& ) = default;
475475 inline MultiDeclarationStatement( MultiDeclarationStatement&& ) = default;
@@ -631,7 +631,7 @@
631631
632632 struct SpecifierString : public COSNode
633633 {
634- inline SpecifierString( depMultiString string = depMultiString() ) noexcept : string( std::move( string ) )
634+ inline SpecifierString( depMultiString string = {} ) noexcept : string( std::move( string ) )
635635 {
636636 return;
637637 }
@@ -642,7 +642,7 @@
642642 };
643643 struct NumberString : public COSNode
644644 {
645- inline NumberString( depMultiString string = depMultiString() ) noexcept : numeric_string( std::move( string ) )
645+ inline NumberString( depMultiString string = {} ) noexcept : numeric_string( std::move( string ) )
646646 {
647647 return;
648648 }
@@ -707,53 +707,66 @@
707707 eOpType op_type = eOpType::UNDEFINED;
708708 };
709709
710-struct PointerTypeSpecifier : public COSNode
710+struct TypeSpecifier : public COSNode
711711 {
712- inline PointerTypeSpecifier( COSNode *spec = nullptr ) noexcept : spec( spec )
712+ inline TypeSpecifier( COSNode *spec = nullptr ) noexcept : spec( spec )
713+ {}
714+ inline TypeSpecifier( const TypeSpecifier& ) = default;
715+ inline TypeSpecifier( TypeSpecifier&& right ) noexcept
713716 {
714- return;
715- }
716- inline PointerTypeSpecifier( const PointerTypeSpecifier& ) = default;
717- inline PointerTypeSpecifier( PointerTypeSpecifier&& right ) noexcept
718- {
719717 this->spec = right.spec;
720718
721719 right.spec = nullptr;
722720 }
723721
724- COSNode *spec;
725-};
726-struct RefTypeSpecifier : public COSNode
727-{
728- inline RefTypeSpecifier( COSNode *spec = nullptr ) noexcept : spec( spec )
729- {}
730- inline RefTypeSpecifier( const RefTypeSpecifier& ) = default;
731- inline RefTypeSpecifier( RefTypeSpecifier&& right ) noexcept
722+ static inline COSNode* ResolveUntilMostSpecialized( COSNode *iter )
732723 {
733- this->spec = right.spec;
724+ while ( true )
725+ {
726+ TypeSpecifier *type_spec = dynamic_cast <TypeSpecifier*> ( iter );
734727
735- right.spec = nullptr;
728+ if ( type_spec == nullptr )
729+ {
730+ return iter;
731+ }
732+
733+ COSNode *more_specialized = type_spec->spec;
734+
735+ if ( more_specialized == nullptr )
736+ {
737+ return iter;
738+ }
739+
740+ iter = more_specialized;
741+ }
742+
743+ return nullptr;
736744 }
737745
738746 COSNode *spec;
739747 };
740-struct ArrayTypeSpecifier : public COSNode
748+struct PointerTypeSpecifier : public TypeSpecifier
741749 {
742- inline ArrayTypeSpecifier( COSNode *spec = nullptr, COSNode *array_size_op = nullptr ) noexcept : spec( spec ), array_size_op( array_size_op )
750+ using TypeSpecifier::TypeSpecifier;
751+};
752+struct RefTypeSpecifier : public TypeSpecifier
753+{
754+ using TypeSpecifier::TypeSpecifier;
755+};
756+struct ArrayTypeSpecifier : public TypeSpecifier
757+{
758+ inline ArrayTypeSpecifier( COSNode *spec = nullptr, COSNode *array_size_op = nullptr ) noexcept : TypeSpecifier( spec ), array_size_op( array_size_op )
743759 {
744760 return;
745761 }
746762 inline ArrayTypeSpecifier( const ArrayTypeSpecifier& ) = default;
747- inline ArrayTypeSpecifier( ArrayTypeSpecifier&& right ) noexcept
763+ inline ArrayTypeSpecifier( ArrayTypeSpecifier&& right ) noexcept : TypeSpecifier( std::move( right ) )
748764 {
749- this->spec = right.spec;
750765 this->array_size_op = right.array_size_op;
751766
752- right.spec = nullptr;
753767 right.array_size_op = nullptr;
754768 }
755769
756- COSNode *spec;
757770 COSNode *array_size_op;
758771 };
759772
@@ -798,7 +811,7 @@
798811 {
799812 FuncsigDefinition *deepest = this;
800813
801- while ( FuncsigDefinition *any_deeper = dynamic_cast <FuncsigDefinition*> ( this->return_type ) )
814+ while ( FuncsigDefinition *any_deeper = dynamic_cast <FuncsigDefinition*> ( TypeSpecifier::ResolveUntilMostSpecialized( deepest->return_type ) ) )
802815 {
803816 deepest = any_deeper;
804817 }
@@ -1160,7 +1173,7 @@
11601173 };
11611174 lexer.GetNamedProduction( "spec" ).GetLastStep()->setSelector <direct_obj_build_selector <char, ResolveSpecifierOperation, COSNode, dynspec_dispatcher, decltype(lexer)::RuntimeEnv>> ( &lexer.GetRuntimeEnvironment() );
11621175 }
1163- assert( lexer.CompileProduction( compiler, "dtypedef", "\"typedef\" spaces tmultdecl spaces ';'" ) == true );
1176+ assert( lexer.CompileProduction( compiler, "dtypedef", "takefirst \"typedef\" spaces tmultdecl spaces ';'" ) == true );
11641177 assert( lexer.CompileProduction( compiler, "block", "'{' spaces [<0>statement:extended statement, spaces] spaces '}'" ) == true );
11651178 assert( lexer.CompileProduction( compiler, "nblock", "'{' spaces [<0>statement:statement, spaces] spaces '}'" ) == true );
11661179 {
@@ -1944,7 +1957,7 @@
19441957 return false;
19451958 }
19461959
1947- inline COSNode* DetachFinishedNode( void )
1960+ inline COSNode* DetachFinishedNode( void ) noexcept
19481961 {
19491962 COSNode *result = this->root;
19501963
@@ -1958,98 +1971,8 @@
19581971 };
19591972 operation.GetLastStep()->setSelector <arrayinit_selector> ();
19601973 }
1961- assert( lexer.CompileInto( compiler, operation, "[\"template\" spaces templop:operation spaces '<' spaces [<0> templarg:procgate type, spaces ',' spaces] spaces endtempl:'>' | dotitem:operation, spaces optok:(\"->\"|'.') spaces]" ) != nullptr );
1962- {
1963- struct dot_with_templ_selector
1964- {
1965- enum class eOpType
1966- {
1967- DOT,
1968- ARROW
1969- };
1974+ assert( lexer.CompileInto( compiler, operation, "&(npgate templinst_templ, itemtempl=operation)" ) != nullptr );
19701975
1971- inline void AddNodeAsRootDot( COSNode *node )
1972- {
1973- COSNode *prev_root = this->root;
1974-
1975- if ( prev_root == nullptr )
1976- {
1977- this->root = node;
1978- return;
1979- }
1980-
1981- eOpType op_type = this->op_type;
1982-
1983- if ( op_type == eOpType::ARROW )
1984- {
1985- this->root = new ArrowOperation( prev_root, node );
1986- }
1987- else if ( op_type == eOpType::DOT )
1988- {
1989- this->root = new DotOperation( prev_root, node );
1990- }
1991- }
1992-
1993- inline void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value )
1994- {
1995- if ( attrib == "endtempl" )
1996- {
1997- this->AddNodeAsRootDot( templop );
1998- this->root = new TemplateInstantiationOperation( this->root, std::move( this->templargs ) );
1999-
2000- this->templop = nullptr;
2001- }
2002- else if ( attrib == "optok" )
2003- {
2004- if ( value == "." )
2005- {
2006- this->op_type = eOpType::DOT;
2007- }
2008- else if ( value == "->" )
2009- {
2010- this->op_type = eOpType::ARROW;
2011- }
2012- }
2013- }
2014-
2015- inline bool AssignNode( const eir::FixedString <char>& attrib, COSNode *node )
2016- {
2017- if ( attrib == "dotitem" )
2018- {
2019- this->AddNodeAsRootDot( node );
2020- return true;
2021- }
2022- else if ( attrib == "templop" )
2023- {
2024- this->templop = node;
2025- return true;
2026- }
2027- else if ( attrib == "templarg" )
2028- {
2029- this->templargs.AddToBack( node );
2030- return true;
2031- }
2032-
2033- return false;
2034- }
2035-
2036- inline COSNode* DetachFinishedNode( void ) noexcept
2037- {
2038- COSNode *result = this->root;
2039-
2040- this->root = nullptr;
2041-
2042- return result;
2043- }
2044-
2045- COSNode *root = nullptr;
2046- COSNode *templop = nullptr;
2047- depVector <COSNode*> templargs;
2048- eOpType op_type = eOpType::DOT;
2049- };
2050- operation.GetLastStep()->setSelector <dot_with_templ_selector> ();
2051- }
2052-
20531976 auto tailop = lexer.MakeStep <decltype(lexer)::RuntimeEnv::StepAlternatives> ();
20541977
20551978 assert( lexer.CompileInto( compiler, tailop, "\"new\" spaces type:spec spaces (curlypack)^0:1" ) != nullptr );
@@ -2505,22 +2428,89 @@
25052428 lexer.GetNamedProduction( "replaceop" ).setSelector <direct_obj_build_selector <char, ReplaceOperation, COSNode, replace_dispatcher, decltype(lexer)::RuntimeEnv>> ( &lexer.GetRuntimeEnvironment() );
25062429 }
25072430 assert( lexer.CompileProduction( compiler, "objmod", "deleteop | replaceop" ) == true );
2508- assert( lexer.CompileProduction( compiler, "npath", "[op:name, spaces optype:'.' spaces]" ) == true );
2431+ assert( lexer.CompileProduction( compiler, "templinst_templ", "[\"template\" spaces dotitem:replaceable itemtempl spaces '<' spaces [<0> templarg:procgate type, spaces ',' spaces] spaces endtempl:'>' | dotitem:replaceable itemtempl, spaces optok:(\"->\"|'.') spaces]" ) == true );
25092432 {
2510- struct npath_dispatcher
2433+ struct dot_with_templ_selector
25112434 {
2435+ enum class eOpType
2436+ {
2437+ DOT,
2438+ ARROW
2439+ };
2440+
2441+ inline void AddNodeAsRootDot( COSNode *node )
2442+ {
2443+ COSNode *prev_root = this->root;
2444+
2445+ if ( prev_root == nullptr )
2446+ {
2447+ this->root = node;
2448+ return;
2449+ }
2450+
2451+ eOpType op_type = this->op_type;
2452+
2453+ if ( op_type == eOpType::ARROW )
2454+ {
2455+ this->root = new ArrowOperation( prev_root, node );
2456+ }
2457+ else if ( op_type == eOpType::DOT )
2458+ {
2459+ this->root = new DotOperation( prev_root, node );
2460+ }
2461+ }
2462+
25122463 inline void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value )
25132464 {
2514- return;
2465+ if ( attrib == "endtempl" )
2466+ {
2467+ this->root = new TemplateInstantiationOperation( this->root, std::move( this->templargs ) );
2468+ }
2469+ else if ( attrib == "optok" )
2470+ {
2471+ if ( value == "." )
2472+ {
2473+ this->op_type = eOpType::DOT;
2474+ }
2475+ else if ( value == "->" )
2476+ {
2477+ this->op_type = eOpType::ARROW;
2478+ }
2479+ }
25152480 }
25162481
2517- inline COSNode* CreateOperation( COSNode *left, COSNode *right )
2482+ inline bool AssignNode( const eir::FixedString <char>& attrib, COSNode *node )
25182483 {
2519- return new DotOperation( left, right );
2484+ if ( attrib == "dotitem" )
2485+ {
2486+ this->AddNodeAsRootDot( node );
2487+ return true;
2488+ }
2489+ else if ( attrib == "templarg" )
2490+ {
2491+ this->templargs.AddToBack( node );
2492+ return true;
2493+ }
2494+
2495+ return false;
25202496 }
2497+
2498+ inline COSNode* DetachFinishedNode( void ) noexcept
2499+ {
2500+ COSNode *result = this->root;
2501+
2502+ this->root = nullptr;
2503+
2504+ return result;
2505+ }
2506+
2507+ COSNode *root = nullptr;
2508+ depVector <COSNode*> templargs;
2509+ eOpType op_type = eOpType::DOT;
25212510 };
2522- lexer.GetNamedProduction( "npath" ).setSelector <left_associative_selector <char, COSNode, npath_dispatcher, decltype(lexer)::RuntimeEnv>> ( &lexer.GetRuntimeEnvironment() );
2511+ lexer.GetNamedProduction( "templinst_templ" ).setSelector <dot_with_templ_selector> ();
25232512 }
2513+ assert( lexer.CompileProduction( compiler, "npath", "&(templinst_templ, itemtempl=name)" ) == true );
25242514 assert( lexer.CompileProduction( compiler, "curlypack", "'(' spaces [<0> param:operation, spaces ',' spaces] spaces ')'" ) == true );
25252515 {
25262516 struct curlypack_dispatcher
@@ -2636,7 +2626,7 @@
26362626
26372627 // Type system COS structures.
26382628 assert( lexer.CompileProduction( compiler, "locator", "[<0>loc:'*',spaces] (spaces loc:'&')^0:1" ) == true ); // helper
2639- assert( lexer.CompileProduction( compiler, "tmultdecl", "utype:spec spaces [locator spaces (func:funcsigdecl|locatedname), spaces nextdef:',' spaces]" ) == true );
2629+ assert( lexer.CompileProduction( compiler, "tmultdecl", "&(mdecltempl, nameplacehold=spec, initplacehold=empty)" ) == true );
26402630 {
26412631 struct tmultdecl_selector
26422632 {
@@ -2646,10 +2636,12 @@
26462636
26472637 COSNode *srctype;
26482638
2649- if ( FuncsigDefinition *func = this->func )
2639+ if ( COSNode *func = this->func )
26502640 {
2651- FuncsigDefinition *left_most_func = func->GetDeepestReturnValueFunctionSignatureType();
2641+ FuncsigDefinition *actual_func = dynamic_cast <FuncsigDefinition*> ( TypeSpecifier::ResolveUntilMostSpecialized( func ) );
26522642
2643+ FuncsigDefinition *left_most_func = actual_func->GetDeepestReturnValueFunctionSignatureType();
2644+
26532645 left_most_func->return_type = underlying_type;
26542646
26552647 srctype = func;
@@ -2675,7 +2667,7 @@
26752667
26762668 inline void AssignAttribute( const eir::FixedString <char>& attrib, const eir::FixedString <char>& value )
26772669 {
2678- if ( attrib == "nextdef" )
2670+ if ( attrib == "nextdecl" )
26792671 {
26802672 this->PushDefinition();
26812673 }
@@ -2694,7 +2686,7 @@
26942686 }
26952687 else if ( attrib == "func" )
26962688 {
2697- this->func = dynamic_cast <FuncsigDefinition*> ( node );
2689+ this->func = node;
26982690 return true;
26992691 }
27002692 else if ( LexingEnvUtils::GetLastAttributionItemInPathFixed( attrib ) == "name" )
@@ -2726,7 +2718,7 @@
27262718 }
27272719
27282720 COSNode *utype = nullptr;
2729- FuncsigDefinition *func = nullptr;
2721+ COSNode *func = nullptr;
27302722 declaratory_locator_builder locbuild;
27312723 TypedefStatement tdef;
27322724 MultiTypedefStatement multidef;
@@ -2751,7 +2743,7 @@
27512743 }
27522744 else if ( attrib == "subfunc" )
27532745 {
2754- this->subfunc = dynamic_cast <FuncsigDefinition*> ( node );
2746+ this->subfunc = node;
27552747 return true;
27562748 }
27572749 else if ( LexingEnvUtils::GetLastAttributionItemInPathFixed( attrib ) == "name" )
@@ -2771,9 +2763,11 @@
27712763
27722764 COSNode *detached_func = this->locbuild.EncapsulateByTypeLocators( new FuncsigDefinition( std::move( this->funcsig ) ) );
27732765
2774- if ( FuncsigDefinition *subfunc = this->subfunc )
2766+ if ( COSNode *subfunc = this->subfunc )
27752767 {
2776- FuncsigDefinition *left_most_sig = subfunc->GetDeepestReturnValueFunctionSignatureType();
2768+ FuncsigDefinition *actual_subfunc = dynamic_cast <FuncsigDefinition*> ( TypeSpecifier::ResolveUntilMostSpecialized( subfunc ) );
2769+
2770+ FuncsigDefinition *left_most_sig = actual_subfunc->GetDeepestReturnValueFunctionSignatureType();
27772771
27782772 left_most_sig->return_type = detached_func;
27792773
@@ -2789,20 +2783,20 @@
27892783
27902784 declaratory_locator_builder locbuild;
27912785 FuncsigDefinition funcsig;
2792- FuncsigDefinition *subfunc = nullptr;
2786+ COSNode *subfunc = nullptr;
27932787 };
27942788 lexer.GetNamedProduction( "funcsigtempl" ).setSelector <funcsig_selector> ();
27952789 }
27962790 assert( lexer.CompileProduction( compiler, "funcsigdecl", "&(funcsigtempl, nameplacehold=spec)" ) == true );
27972791 assert( lexer.CompileProduction( compiler, "locnametempl", "name:replaceable nameplacehold (spaces [arrspec:arrayspec, spaces])^0:1" ) == true ); // helper
2798- assert( lexer.CompileProduction( compiler, "locatedname", "&(locnametempl, nameplacehold=spec)" ) == true );
27992792 assert( lexer.CompileProduction( compiler, "arrayspec", "'[' spaces (idx:operation spaces)^0:1 endtok:']'" ) == true ); // helper
28002793 // Set-up both of the following productions in one block.
28012794 assert( lexer.CompileProduction( compiler, "decltemplate", "utype:spec spaces locator spaces (func:funcsigtempl|locnametempl) replaceable initplacehold" ) == true ); // helper
2795+ assert( lexer.CompileProduction( compiler, "mdecltempl", "utype:spec spaces [locator spaces (func:funcsigtempl|locnametempl) replaceable initplacehold, spaces nextdecl:',' spaces]" ) == true );
28022796 assert( lexer.CompileProduction( compiler, "declaration", "&(decltemplate, nameplacehold=spec, initplacehold=declinit)" ) == true );
28032797 assert( lexer.CompileProduction( compiler, "decl_optnoinit", "&(decltemplate, nameplacehold=(spec)^0:1, initplacehold=empty)" ) == true );
28042798 assert( lexer.CompileProduction( compiler, "decl_opt", "&(decltemplate, nameplacehold=(spec)^0:1, initplacehold=declinit)" ) == true );
2805- assert( lexer.CompileProduction( compiler, "multdecl", "utype:spec spaces [locator spaces (func:funcsigdecl|locatedname) declinit, spaces nextdecl:',' spaces] mdeclterm" ) == true );
2799+ assert( lexer.CompileProduction( compiler, "multdecl", "&(mdecltempl, nameplacehold=spec, initplacehold=declinit) mdeclterm" ) == true );
28062800 {
28072801 struct declaration_selector
28082802 {
@@ -2820,7 +2814,7 @@
28202814 }
28212815 else if ( attrib == "func" )
28222816 {
2823- this->func = dynamic_cast <FuncsigDefinition*> ( node );
2817+ this->func = node;
28242818 return true;
28252819 }
28262820 else if ( attrib == "init" )
@@ -2856,10 +2850,12 @@
28562850
28572851 COSNode *built_type;
28582852
2859- if ( FuncsigDefinition *func = this->func )
2853+ if ( COSNode *func = this->func )
28602854 {
2861- FuncsigDefinition *left_most_sig = func->GetDeepestReturnValueFunctionSignatureType();
2855+ FuncsigDefinition *actual_func = dynamic_cast <FuncsigDefinition*> ( TypeSpecifier::ResolveUntilMostSpecialized( func ) );
28622856
2857+ FuncsigDefinition *left_most_sig = actual_func->GetDeepestReturnValueFunctionSignatureType();
2858+
28632859 left_most_sig->return_type = underlying_type;
28642860
28652861 built_type = func;
@@ -2894,7 +2890,7 @@
28942890 }
28952891
28962892 COSNode *utype = nullptr;
2897- FuncsigDefinition *func = nullptr;
2893+ COSNode *func = nullptr;
28982894 declaratory_locator_builder locbuild;
28992895 DeclarationStatement decl;
29002896 bool special_init = false;
@@ -3021,18 +3017,10 @@
30213017 {
30223018 init_depth--;
30233019
3024- if ( PointerTypeSpecifier *ptrspec = dynamic_cast <PointerTypeSpecifier*> ( init_type ) )
3020+ if ( TypeSpecifier *ptrspec = dynamic_cast <TypeSpecifier*> ( init_type ) )
30253021 {
30263022 init_type = ptrspec->spec;
30273023 }
3028- else if ( RefTypeSpecifier *refspec = dynamic_cast <RefTypeSpecifier*> ( init_type ) )
3029- {
3030- init_type = refspec->spec;
3031- }
3032- else if ( ArrayTypeSpecifier *arrspec = dynamic_cast <ArrayTypeSpecifier*> ( init_type ) )
3033- {
3034- init_type = arrspec->spec;
3035- }
30363024 else
30373025 {
30383026 // Unknown.
@@ -3111,7 +3099,7 @@
31113099 }
31123100 assert( lexer.CompileProduction( compiler, "declinit", "( spaces init_s:initexpr | spaces '=' spaces init:operation | spaces init:curlypack )^0:1" ) == true );
31133101 assert( lexer.CompileProduction( compiler, "functag", "\"func\" | \"proc\"" ) == true );
3114- assert( lexer.CompileProduction( compiler, "type", "&(decltemplate, nameplacehold=spec, initplacehold=empty)" ) == true );
3102+ assert( lexer.CompileProduction( compiler, "type", "&(decltemplate, nameplacehold=empty, initplacehold=empty)" ) == true );
31153103 {
31163104 struct type_selector
31173105 {
@@ -3129,7 +3117,7 @@
31293117 }
31303118 else if ( attrib == "func" )
31313119 {
3132- this->func = dynamic_cast <FuncsigDefinition*> ( node );
3120+ this->func = node;
31333121 return true;
31343122 }
31353123 return locbuild.AssignNode( attrib, node );
@@ -3139,10 +3127,12 @@
31393127 {
31403128 COSNode *underlying_type = this->locbuild.EncapsulateByTypeLocators( this->utype );
31413129
3142- if ( FuncsigDefinition *func = this->func )
3130+ if ( COSNode *func = this->func )
31433131 {
3144- FuncsigDefinition *left_most_func = func->GetDeepestReturnValueFunctionSignatureType();
3132+ FuncsigDefinition *actual_func = dynamic_cast <FuncsigDefinition*> ( TypeSpecifier::ResolveUntilMostSpecialized( func ) );
31453133
3134+ FuncsigDefinition *left_most_func = actual_func->GetDeepestReturnValueFunctionSignatureType();
3135+
31463136 left_most_func->return_type = underlying_type;
31473137
31483138 return func;
@@ -3155,7 +3145,7 @@
31553145
31563146 COSNode *utype = nullptr;
31573147 declaratory_locator_builder locbuild;
3158- FuncsigDefinition *func = nullptr;
3148+ COSNode *func = nullptr;
31593149 };
31603150 lexer.GetNamedProduction( "type" ).setSelector <type_selector> ();
31613151 }
@@ -7712,4 +7702,233 @@
77127702 }
77137703 }
77147704 printf( "ok.\n" );
7705+
7706+ printf( "testing COS template instantiation..." );
7707+ {
7708+ // 1)
7709+ {
7710+ COSNode *_root = lexer.StartProduction( "template variation <int> var;" );
7711+
7712+ DeclarationStatement *root = dynamic_cast <DeclarationStatement*> ( _root );
7713+
7714+ assert( root != nullptr );
7715+ assert( root->initializer == nullptr );
7716+
7717+ SpecifierString *name = dynamic_cast <SpecifierString*> ( root->name );
7718+
7719+ assert( name != nullptr );
7720+ assert( name->string == "var" );
7721+
7722+ name->Delete();
7723+
7724+ TemplateInstantiationOperation *type = dynamic_cast <TemplateInstantiationOperation*> ( root->type );
7725+
7726+ assert( type != nullptr );
7727+ assert( type->tparams.GetCount() == 1 );
7728+
7729+ SpecifierString *type_p1 = dynamic_cast <SpecifierString*> ( type->tparams[0] );
7730+
7731+ assert( type_p1 != nullptr );
7732+ assert( type_p1->string == "int" );
7733+
7734+ type_p1->Delete();
7735+
7736+ SpecifierString *type_template = dynamic_cast <SpecifierString*> ( type->templ );
7737+
7738+ assert( type_template != nullptr );
7739+ assert( type_template->string == "variation" );
7740+
7741+ type_template->Delete();
7742+ type->Delete();
7743+ root->Delete();
7744+ }
7745+
7746+ // 2)
7747+ {
7748+ COSNode *_root = lexer.StartProduction( "container.template memb <float>.value;" );
7749+
7750+ DotOperation *root = dynamic_cast <DotOperation*> ( _root );
7751+
7752+ assert( root != nullptr );
7753+
7754+ SpecifierString *right = dynamic_cast <SpecifierString*> ( root->right );
7755+
7756+ assert( right != nullptr );
7757+ assert( right->string == "value" );
7758+
7759+ right->Delete();
7760+
7761+ TemplateInstantiationOperation *left = dynamic_cast <TemplateInstantiationOperation*> ( root->left );
7762+
7763+ assert( left != nullptr );
7764+ assert( left->tparams.GetCount() == 1 );
7765+
7766+ DotOperation *left_templ = dynamic_cast <DotOperation*> ( left->templ );
7767+
7768+ assert( left_templ != nullptr );
7769+
7770+ SpecifierString *left_templ_left = dynamic_cast <SpecifierString*> ( left_templ->left );
7771+
7772+ assert( left_templ_left != nullptr );
7773+ assert( left_templ_left->string == "container" );
7774+
7775+ left_templ_left->Delete();
7776+
7777+ SpecifierString *left_templ_right = dynamic_cast <SpecifierString*> ( left_templ->right );
7778+
7779+ assert( left_templ_right != nullptr );
7780+ assert( left_templ_right->string == "memb" );
7781+
7782+ left_templ_right->Delete();
7783+ left_templ->Delete();
7784+
7785+ SpecifierString *left_p1 = dynamic_cast <SpecifierString*> ( left->tparams[0] );
7786+
7787+ assert( left_p1 != nullptr );
7788+ assert( left_p1->string == "float" );
7789+
7790+ left_p1->Delete();
7791+ left->Delete();
7792+ root->Delete();
7793+ }
7794+
7795+ // 3)
7796+ {
7797+ COSNode *_root = lexer.StartProduction( "template (template meta <void>) <int>;" );
7798+
7799+ TemplateInstantiationOperation *root = dynamic_cast <TemplateInstantiationOperation*> ( _root );
7800+
7801+ assert( root != nullptr );
7802+ assert( root->tparams.GetCount() == 1 );
7803+
7804+ SpecifierString *p1 = dynamic_cast <SpecifierString*> ( root->tparams[0] );
7805+
7806+ assert( p1 != nullptr );
7807+ assert( p1->string == "int" );
7808+
7809+ p1->Delete();
7810+
7811+ TemplateInstantiationOperation *templ = dynamic_cast <TemplateInstantiationOperation*> ( root->templ );
7812+
7813+ assert( templ != nullptr );
7814+ assert( templ->tparams.GetCount() == 1 );
7815+
7816+ SpecifierString *templ_p1 = dynamic_cast <SpecifierString*> ( templ->tparams[0] );
7817+
7818+ assert( templ_p1 != nullptr );
7819+ assert( templ_p1->string == "void" );
7820+
7821+ templ_p1->Delete();
7822+
7823+ SpecifierString *templ_templ = dynamic_cast <SpecifierString*> ( templ->templ );
7824+
7825+ assert( templ_templ != nullptr );
7826+ assert( templ_templ->string == "meta" );
7827+
7828+ templ_templ->Delete();
7829+ templ->Delete();
7830+ root->Delete();
7831+ }
7832+
7833+ // 4)
7834+ {
7835+ COSNode *_root = lexer.StartProduction( "template first <int>.template second <long>;" );
7836+
7837+ TemplateInstantiationOperation *root = dynamic_cast <TemplateInstantiationOperation*> ( _root );
7838+
7839+ assert( root != nullptr );
7840+ assert( root->tparams.GetCount() == 1 );
7841+
7842+ SpecifierString *p1 = dynamic_cast <SpecifierString*> ( root->tparams[0] );
7843+
7844+ assert( p1 != nullptr );
7845+ assert( p1->string == "long" );
7846+
7847+ p1->Delete();
7848+
7849+ DotOperation *templ = dynamic_cast <DotOperation*> ( root->templ );
7850+
7851+ assert( templ != nullptr );
7852+
7853+ SpecifierString *templ_right = dynamic_cast <SpecifierString*> ( templ->right );
7854+
7855+ assert( templ_right != nullptr );
7856+ assert( templ_right->string == "second" );
7857+
7858+ templ_right->Delete();
7859+
7860+ TemplateInstantiationOperation *templ_left = dynamic_cast <TemplateInstantiationOperation*> ( templ->left );
7861+
7862+ assert( templ_left != nullptr );
7863+ assert( templ_left->tparams.GetCount() == 1 );
7864+
7865+ SpecifierString *templ_left_templ = dynamic_cast <SpecifierString*> ( templ_left->templ );
7866+
7867+ assert( templ_left_templ != nullptr );
7868+ assert( templ_left_templ->string == "first" );
7869+
7870+ templ_left_templ->Delete();
7871+
7872+ SpecifierString *templ_left_p1 = dynamic_cast <SpecifierString*> ( templ_left->tparams[0] );
7873+
7874+ assert( templ_left_p1 != nullptr );
7875+ assert( templ_left_p1->string == "int" );
7876+
7877+ templ_left_p1->Delete();
7878+ templ_left->Delete();
7879+ templ->Delete();
7880+ root->Delete();
7881+ }
7882+
7883+ // 5)
7884+ {
7885+ COSNode *_root = lexer.StartProduction( "template (template first <int>.second) <long>;" );
7886+
7887+ TemplateInstantiationOperation *root = dynamic_cast <TemplateInstantiationOperation*> ( _root );
7888+
7889+ assert( root != nullptr );
7890+ assert( root->tparams.GetCount() == 1 );
7891+
7892+ SpecifierString *p1 = dynamic_cast <SpecifierString*> ( root->tparams[0] );
7893+
7894+ assert( p1 != nullptr );
7895+ assert( p1->string == "long" );
7896+
7897+ p1->Delete();
7898+
7899+ DotOperation *templ = dynamic_cast <DotOperation*> ( root->templ );
7900+
7901+ assert( templ != nullptr );
7902+
7903+ SpecifierString *templ_right = dynamic_cast <SpecifierString*> ( templ->right );
7904+
7905+ assert( templ_right != nullptr );
7906+ assert( templ_right->string == "second" );
7907+
7908+ templ_right->Delete();
7909+
7910+ TemplateInstantiationOperation *templ_left = dynamic_cast <TemplateInstantiationOperation*> ( templ->left );
7911+
7912+ assert( templ_left != nullptr );
7913+ assert( templ_left->tparams.GetCount() == 1 );
7914+
7915+ SpecifierString *templ_left_templ = dynamic_cast <SpecifierString*> ( templ_left->templ );
7916+
7917+ assert( templ_left_templ != nullptr );
7918+ assert( templ_left_templ->string == "first" );
7919+
7920+ templ_left_templ->Delete();
7921+
7922+ SpecifierString *templ_left_p1 = dynamic_cast <SpecifierString*> ( templ_left->tparams[0] );
7923+
7924+ assert( templ_left_p1 != nullptr );
7925+ assert( templ_left_p1->string == "int" );
7926+
7927+ templ_left_p1->Delete();
7928+ templ_left->Delete();
7929+ templ->Delete();
7930+ root->Delete();
7931+ }
7932+ }
7933+ printf( "ok.\n" );
77157934 }
\ No newline at end of file
--- testcompiler/src/lexingenv_tests.cpp (revision 69)
+++ testcompiler/src/lexingenv_tests.cpp (revision 70)
@@ -1132,7 +1132,7 @@
11321132 printf( "testing ProductionMachine StepProceduralAlternatives..." );
11331133 {
11341134 // This step is no-longer considered the optimal way for operation associativity anymore.
1135- // It does allow you to put the burder of associativity into solely the lexer, but it does
1135+ // It does allow you to put the burden of associativity into solely the lexer, but it does
11361136 // come with a performancy penalty of reading in same data redundantly many times.
11371137 // Instead, the new and optimal way to lex such ambiguous and recursive syntax/data structures
11381138 // is the selector-based node concept! (StepProceduralSequence is related)
@@ -2573,9 +2573,13 @@
25732573
25742574 TestAllocEnv::ProductionMachine machine( &env );
25752575
2576+ machine.Execute( "testtest", &producible );
2577+
2578+ assert( machine.successful == true );
2579+
25762580 machine.Execute( "testmeow", &producible );
25772581
2578- assert( machine.successful == true );
2582+ assert( machine.successful == true ); // this is critical.
25792583 }
25802584 printf( "ok.\n" );
25812585
@@ -6410,7 +6414,7 @@
64106414
64116415 printf( "testing ProductionMachine StepDisabled..." );
64126416 {
6413- // There is not really much to test other than the machine does process it.
6417+ // There is not really much to test other than that the machine does process it.
64146418
64156419 TestAllocEnv env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
64166420
@@ -7139,4 +7143,113 @@
71397143 assert( has_received_attrib == true );
71407144 }
71417145 printf( "ok.\n" );
7146+
7147+ printf( "testing ProductionMachine grammar-graph depth no automatic empty/denial invalidation..." );
7148+ {
7149+ // The CFG grammar development does heavily suffer from the realities of the P != NP problem.
7150+ // As a (distant) result, grammar productions whose lexical strength changes depending on high-level
7151+ // rules (replacement, extension, etc) cannot provide the non-empty/halting problem guarantee in
7152+ // lexers without guidance. This difficult reality shall be described in this unit test, by the use
7153+ // of replacement-points.
7154+
7155+ TestAllocEnv env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
7156+
7157+ decltype(env)::StepToken tok1( &env );
7158+ tok1.token_str = "prim";
7159+
7160+ decltype(env)::StepToken tok2( &env );
7161+ tok2.token_str = "sec";
7162+
7163+ decltype(env)::StepAttribution replaceable_tok( &env );
7164+ replaceable_tok.SetNode( &tok1 );
7165+ replaceable_tok.replaceable = true;
7166+
7167+ decltype(env)::StepReplacementPoint replpt( &env );
7168+ replpt.SetNode( &replaceable_tok );
7169+ replpt.SetReplacement( &tok1, &tok2 );
7170+
7171+ decltype(env)::StepAlternatives producible( &env );
7172+ producible.SetConnections( { &replaceable_tok, &replpt } );
7173+
7174+ decltype(env)::ProductionMachine machine( &env );
7175+
7176+ machine.Execute( "prim", &producible );
7177+
7178+ assert( machine.successful == true );
7179+
7180+ machine.Execute( "sec", &producible );
7181+
7182+ // While your intuition might say that the following should succeed, for reasons
7183+ // above it cannot under the defaulted rules as written down in this unit test.
7184+ // A good solution to this problem would be guided-grammar using "no-progress-gates".
7185+ // Assuming change of the grammar graph after each replacement-point would be a possible
7186+ // but too risky change because it could delude developers into thinking that grammar
7187+ // development is trivial.
7188+ assert( machine.successful == false );
7189+ }
7190+ printf( "ok.\n" );
7191+
7192+ printf( "testing ProductionMachine repeat-evaluation of deeply-changing grammar nodes using npgates..." );
7193+ {
7194+ // This unit test is meant to show the fixed version of above's grammar using no-progress-gates.
7195+
7196+ TestAllocEnv env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
7197+
7198+ decltype(env)::StepToken tok1( &env );
7199+ tok1.token_str = "prim";
7200+
7201+ decltype(env)::StepToken tok2( &env );
7202+ tok2.token_str = "sec";
7203+
7204+ decltype(env)::StepAttribution replaceable_tok( &env );
7205+ replaceable_tok.SetNode( &tok1 );
7206+ replaceable_tok.replaceable = true;
7207+ replaceable_tok.npgate = true; // entering this step should be unaffected by no-progress; subsequent step should recalculate
7208+
7209+ decltype(env)::StepReplacementPoint replpt( &env );
7210+ replpt.SetNode( &replaceable_tok );
7211+ replpt.SetReplacement( &tok1, &tok2 );
7212+
7213+ decltype(env)::StepAlternatives producible( &env );
7214+ producible.SetConnections( { &replaceable_tok, &replpt } );
7215+
7216+ decltype(env)::ProductionMachine machine( &env );
7217+
7218+ machine.Execute( "prim", &producible );
7219+
7220+ assert( machine.successful == true );
7221+
7222+ machine.Execute( "sec", &producible );
7223+
7224+ // Does match intuition thanks to the use of a no-progress-gate (npgate).
7225+ assert( machine.successful == true );
7226+ }
7227+ printf( "ok.\n" );
7228+
7229+ printf( "testing ProductionMachine wrong-enter-fail handling of StepSeparation (forced parent fail if sub-step enter-fail)..." );
7230+ {
7231+ // For some step types we could have wrongly implemented the failed-enter.
7232+ // This test checks if we did not mess up the separation-step.
7233+
7234+ TestAllocEnv env( eir::constr_with_alloc::DEFAULT, &globalHeapAlloc );
7235+
7236+ decltype(env)::StepToken tok( &env );
7237+ tok.token_str = "meow";
7238+
7239+ decltype(env)::StepSeparation sep( &env );
7240+ sep.SetItemStep( &tok );
7241+ sep.SetSeparationStep( &tok );
7242+ sep.min_item_cnt = 0;
7243+ sep.is_alternation = false;
7244+
7245+ decltype(env)::StepAlternatives producible( &env );
7246+ producible.SetConnections( { &tok, &sep } );
7247+
7248+ decltype(env)::ProductionMachine machine( &env );
7249+
7250+ machine.Execute( "", &producible );
7251+
7252+ assert( machine.successful == true );
7253+ }
7254+ printf( "ok.\n" );
71427255 }
\ No newline at end of file
Show on old repository browser