ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
program.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !DOXYGEN
12#endif // !DOXYGEN
13
14namespace alib { namespace expressions { namespace detail {
15
16//! @cond NO_DOX
17using Command= VirtualMachine::Command;
18//! @endcond
19
20// #################################################################################################
21// Program
22// #################################################################################################
23
24#if ALIB_DEBUG
25# define DBG_SET_CALLBACK_INFO \
26 prg.act().DbgInfo.Callback= cInfo.DbgCallbackName; \
27 prg.act().DbgInfo.Plugin = ppp.plugin;
28#else
29# define DBG_SET_CALLBACK_INFO
30#endif
31
32// Use temporary allocator for program vector construction.
33// In method AssembleFinalize, this will be re-allocated into ctScope
34Program::Program( Compiler& pCompiler, ExpressionVal& pExpression, MonoAllocator* ctAlloc )
35: compiler ( pCompiler )
36, expression ( pExpression )
37, ctNestedExpressions( pExpression.allocator )
38, qtyOptimizations ( HasBits( compiler.CfgCompilation, Compilation::NoOptimization )
39 ? -1 : 0 )
40, compileStorage ( ctAlloc ? (*ctAlloc)().New<CompileStorage>(*ctAlloc) : nullptr )
41{}
42
48
49
50// #################################################################################################
51// Helpers used during compilation
52// #################################################################################################
53#if !DOXYGEN
54namespace {
55struct Assembly
56{
57 using VM= VirtualMachine;
58
60 StdVectorMono<VM::PC >& resultStack;
61
63 Assembly( StdVectorMono<VirtualMachine::Command*>& pAssembly,
64 StdVectorMono<VM::PC >& pResultStack )
65 : assembly ( pAssembly )
66 , resultStack( pResultStack) {}
67
68 /// Returns the current last command.
69 /// @return A reference to the last command.
71 integer length()
72 {
73 return static_cast<integer>(assembly.size());
74 }
75
76 /// Returns the command at the given program counter \p{pc}.
77 /// @param pc The program counter of the command to get.
78 /// @return A reference to the requested command.
80 VM::Command& at( VM::PC pc )
81 {
82 return *assembly[size_t(pc)];
83 }
84
85 /// Returns the current last command.
86 /// @return A reference to the last command.
88 VM::Command& act()
89 {
90 return *assembly.back();
91 }
92
93 /// Returns the current last command.
94 /// @return A reference to the last command.
96 VM::Command& prev()
97 {
98 return **( assembly.end() - 2 );
99 }
100
101 /// Returns the number of the last command.
102 /// @return The current size of the program minus \c 1.
104 VM::PC actPC()
105 {
106 return static_cast<VM::PC>( assembly.size() - 1);
107 }
108
109 /// Removes the last command.
111 void eraseLast()
112 {
113 assembly.pop_back();
114 }
115
116 /// Removes a command.
117 /// @param pc The command to remove.
119 void erase( VM::PC pc )
120 {
121 assembly.erase( assembly.begin() + pc );
122
123 }
124
125 /// Removes range of commands.
126 /// @param begin The first command to remove.
127 /// @param end The first command that is not removed.
129 void erase( VM::PC begin, VM::PC end )
130 {
131 assembly.erase( assembly.begin() + begin,
132 assembly.begin() + end );
133 }
134
135
136 /// Inserts a command at the given position.
137 /// @tparam TArgs Types of the variadic arguments \p{args}.
138 /// @param pc The position of insertion.
139 /// @param args Variadic arguments forwarded to the command's constructor.
140 /// @return The command inserted.
141 template<typename... TArgs>
143 VM::Command& insertAt( VM::PC pc, TArgs && ... args )
144 {
145 auto* cmd= assembly.get_allocator().AI().New<VM::Command>(std::forward<TArgs>( args )... );
146 assembly.emplace( assembly.begin() + pc, cmd );
147 return *cmd;
148 }
149
150 /// Inserts a command at the end of the program.
151 /// @tparam TArgs Types of the variadic arguments \p{args}.
152 /// @param args Variadic arguments forwarded to the command's constructor.
153 /// @return The command inserted.
154 template<typename... TArgs>
156 VM::Command& add( TArgs && ... args )
157 {
158 assembly.emplace_back( assembly.get_allocator().AI().New<VM::Command>( std::forward<TArgs>( args )... ) );
159 return *assembly.back();
160 }
161
162
163 /// Pushes the current PC to the result stack.
165 void pushResultPC()
166 {
167 resultStack.push_back( actPC() );
168 }
169
170 /// Pops one from the result stack.
172 void popResultPC()
173 {
174 resultStack.pop_back();
175 }
176
177 /// Returns a mutable reference to the top of the stack of result positions.
179 VM::PC & resultPC()
180 {
181 return resultStack.back();
182 }
183
184 /// Returns a mutable reference to the 2nd.-top of the stack of result positions.
186 VM::PC & lhsResultPC()
187 {
188 return resultStack[resultStack.size() - 2];
189 }
190
191 /// Returns the program counter that identifies the start of the range that results in the
192 /// current LHS value.
194 VM::PC lhsResultStartPC()
195 {
196 auto qtyResults = resultStack.size();
197 return qtyResults == 2 ? 0
198 : resultStack[qtyResults - 3] + 1; // one after the previous
199 }
200};
201} // anonymous namespace
202#endif // ALIB_DOX
203
204// #################################################################################################
205// Assemble commands
206// #################################################################################################
207
208#define ASSERT_ASSEMBLE \
209 ALIB_ASSERT_ERROR( prg.resultStack.empty() \
210 || prg.resultPC() == prg.actPC() \
211 || prg.act().IsJump() , \
212 "EXPR", "Internal error: Last in result stack is not last command." )
213
215{
216 auto& stack= expression.ctScope->Stack;
217 ALIB_ASSERT_ERROR( compileStorage->ResultStack.size() >= size_t( qty < 0 ? 0 : qty ),
218 "EXPR", "Internal error. This should never happen." ) // not enaugh arguments on the stack
219
220 stack->clear();
221 if( qty > 0 )
222 stack->reserve( size_t( qty ));
223
224 bool allAreConst= true;
225 for( integer i= qty; i > 0 ; --i )
226 {
227 Command& cmd= *compileStorage->Assembly[size_t(*(compileStorage->ResultStack.end() - i))];
228 bool isConstant= cmd.IsConstant();
229 stack->emplace_back( cmd.ResultType );
230 allAreConst&= isConstant;
231 }
232
233 return allAreConst && !HasBits(compiler.CfgCompilation, Compilation::NoOptimization );
234}
235
236
237void Program::AssembleConstant( Box& value, integer idxInOriginal, integer idxInNormalized )
238{
239 if( compileStorage == nullptr )
240 return;
242 ASSERT_ASSEMBLE
243
244 prg.add( VM::Command( value.IsType<String>() ? String( expression.allocator, value.Unbox<String>() )
245 : value,
246 false, idxInOriginal, idxInNormalized ) );
247 prg.pushResultPC();
248}
249
250
251void Program::AssembleFunction( AString& functionName , bool isIdentifier, int qtyArgs,
252 integer idxInOriginal, integer idxInNormalized )
253{
254 if( compileStorage == nullptr )
255 return;
257 ASSERT_ASSEMBLE
258
259 // Nested expressions
261 && compiler.CfgNestedExpressionFunction.Match(functionName ) )
262 {
264
266 ? 1 : 2 )
267 || !prg.at( *(prg.resultStack.end() - ( qtyArgs == 3 ? 2 : qtyArgs) ) )
268 .ResultType.IsType<String>() )
269 {
272 }
273
274
275 // single argument? -> we have to get the expression now
276 if( qtyArgs == 1 )
277 {
278 if( !prg.at(compileStorage->ResultStack.back()).IsConstant() ) // must not use bool allAreConstant here!
279 {
282 expression.GetOriginalString(), idxInOriginal );
283 throw e;
284 }
285
286 Expression nested;
287 String nestedExpressionName= prg.at(prg.resultStack.back()).ResultType.Unbox<String>();
288 try
289 {
290 nested= compiler.GetNamed( nestedExpressionName );
291 }
292 catch( Exception& e )
293 {
294 if( e.Type().Integral() == UnderlyingIntegral( Exceptions::NamedExpressionNotFound ) )
295 {
297 nestedExpressionName );
298 }
299 else
300 {
301 ALIB_ERROR( "EXPR", "Unknown exception {!Q}.", e.Type() )
302 }
303 throw;
304 }
305
306 ctNestedExpressions.emplace_back(nested);
307
308 prg.act()= VM::Command( nested->GetProgram(),
309 nested->ResultType(),
311 idxInOriginal, idxInNormalized );
312 return;
313 }
314
315 // If two arguments, we send nullptr to indicate that 2nd argument is replacement
316 if( qtyArgs == 2 )
317 {
318 prg.add( static_cast<Program*>( nullptr ), Box( nullptr ),
320 idxInOriginal, idxInNormalized );
321 prg.act().ResultType= prg.prev().ResultType;
322 }
323
324 // 3rd argument given (throw): we send "this" which indicates to throw if an
325 // expression is not found.
326 else
327 prg.add( dynamic_cast<Program*>( this ),
328 prg.act().ResultType,
330 idxInOriginal, idxInNormalized );
331
332 prg.popResultPC();
333 prg.resultPC()= prg.actPC();
334 return;
335 }
336
337
338 // collect arguments
339 bool allAreConstant= collectArgs(qtyArgs);
340
343 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
344 functionName, isIdentifier,
345 allAreConstant,
347
348 try
349 {
350 for( auto& ppp : compiler.plugins )
351 {
352 if( !ppp.plugin->TryCompilation( cInfo ) )
353 continue;
354
355 // constant?
356 if( cInfo.Callback == nullptr )
357 {
358 if( qtyArgs > 0 )
360
361 // remove constant vm commands, add new and update result stack
362 if( expression.ctScope->Stack->size() == 0 )
363 {
364 prg.add( VM::Command( cInfo.TypeOrValue, true, idxInOriginal, idxInNormalized ) );
365 prg.pushResultPC();
366 }
367 else
368 {
369 for( auto i= expression.ctScope->Stack->size() - 1; i > 0 ; --i )
370 {
371 prg.eraseLast();
372 prg.popResultPC();
373 }
374 prg.resultPC()= prg.actPC();
375 prg.act()= VM::Command( cInfo.TypeOrValue, true, idxInOriginal, idxInNormalized );
376 }
377
378 DBG_SET_CALLBACK_INFO
379
380 return;
381 }
382
383 // function
384 prg.add( cInfo.Callback, isIdentifier, qtyArgs, cInfo.TypeOrValue,
385 String(expression.allocator, functionName), false,
386 idxInOriginal, idxInNormalized );
387
388 // update result type stack
389 if( expression.ctScope->Stack->size()== 0 )
390 prg.pushResultPC();
391 else
392 {
393 for( auto i= expression.ctScope->Stack->size() - 1; i > 0 ; --i )
394 prg.popResultPC();
395 prg.resultPC()= prg.actPC();
396 }
397
398 DBG_SET_CALLBACK_INFO
399 return;
400 }
401 }
402 catch( Exception& e )
403 {
405 && !e.Type().IsEnumType<Exceptions>() )
407
409 throw;
410 }
411 catch( std::exception& stdException )
412 {
414 {
417 expression.GetOriginalString(), idxInOriginal );
418 e.Add ( ALIB_CALLER_NULLED, Exceptions::StdExceptionInfo, stdException.what() );
419 throw e;
420 }
421 throw;
422 }
423
424
425 // create identifier exception
426 if( isIdentifier )
428
429 // create function exception
430 String128 arguments;
433 expression.ctScope->Stack->end(), arguments );
434
435 Exception e( ALIB_CALLER_NULLED, Exceptions::UnknownFunction, functionName, arguments );
436
437 for( auto& notMatchedName : cInfo.FunctionsWithNonMatchingArguments )
438 e.Add( ALIB_CALLER_NULLED, Exceptions::FunctionHint, notMatchedName );
439
440 throw e;
441}
442
443void Program::AssembleUnaryOp( String& op, integer idxInOriginal, integer idxInNormalized )
444{
445 if( compileStorage == nullptr )
446 return;
448 ASSERT_ASSEMBLE
449
450 // If we have a global operator replacement, this will be used. However, in this case changes
451 // of it must not be passed back to the caller as long certain normalization flags are set.
452 String opReference= op;
453 bool aliased= false;
454 auto globalAliasIt= compiler.AlphabeticUnaryOperatorAliases.Find(op);
455 if( globalAliasIt != compiler.AlphabeticUnaryOperatorAliases.end() )
456 {
457 aliased= true;
458 opReference= globalAliasIt.Mapped();
459 }
460
461 bool isConstant= collectArgs(1);
462
463 // Nested expressions
465 && opReference == compiler.CfgNestedExpressionOperator
466 && expression.ctScope->Stack->back().IsType<String>() )
467 {
468 if( !prg.at(compileStorage->ResultStack.back()).IsConstant() ) // must not use bool allAreConstant here!
469 {
472 expression.GetOriginalString(), idxInOriginal );
473 throw e;
474 }
475
476 String expressionName= expression.ctScope->Stack->back().Unbox<String>();
477 Expression nested;
478 try
479 {
480 nested= compiler.GetNamed( expressionName );
481 }
482 catch( Exception& e )
483 {
484 if( e.Type().Integral() == UnderlyingIntegral( Exceptions::NamedExpressionNotFound ) )
486 expressionName );
487 else
488 {
489 ALIB_ERROR( "EXPR", "Unknown exception {!Q}.", e.Type() )
490 }
491 throw;
492 }
493
495 op= opReference;
496 else
498 op= globalAliasIt->first;
499
500 ctNestedExpressions.emplace_back(nested);
501 prg.act()= VM::Command( nested->GetProgram(),
502 nested->ResultType(),
503 op,
504 idxInOriginal, idxInNormalized );
505 return;
506 }
507
508
509 try
510 {
511 for( int pass= 0; pass < 2 ; ++pass )
512 {
513 isConstant= collectArgs(1);
515 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
516 opReference, isConstant );
517
518 // search plug-ins
519 for( auto& ppp : compiler.plugins )
520 {
521 if( !ppp.plugin->TryCompilation( cInfo ) )
522 continue;
523
525 op= opReference;
526 else
528 op= globalAliasIt->first;
529
530 // constant?
531 if( !cInfo.Callback )
532 {
534
535 // replace last command (unary op arg is always the last)
536 prg.act()= VM::Command( cInfo.TypeOrValue, true, idxInOriginal, idxInNormalized );
537
538 DBG_SET_CALLBACK_INFO
539
540 return;
541 }
542
543 // callback
544 prg.add( cInfo.Callback, false, 1, cInfo.TypeOrValue, op, true, idxInOriginal, idxInNormalized );
545 ++prg.resultPC();
546
547 DBG_SET_CALLBACK_INFO
548 return;
549 }
550
551 // did we try auto cast already?
552 if( pass )
553 break;
554
555 // try auto cast
557 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
558 op,
559 prg.at( prg.resultPC() ).IsConstant(), false );
560 for( auto& pppAutoCast : compiler.plugins )
561 {
562 if( !pppAutoCast.plugin->TryCompilation( ciAutoCast ) )
563 continue;
564
565 // cast found?
566 if( !ciAutoCast.TypeOrValue.IsType<void>() )
567 {
568 // const?
569 if( ciAutoCast.Callback == nullptr )
570 {
571 // change constant value conversion
572 VM::Command& cmdToPatch= prg.at(prg.resultPC());
573 cmdToPatch.ResultType= ciAutoCast.TypeOrValue;
574 ALIB_DBG( cmdToPatch.DbgInfo.Plugin= pppAutoCast.plugin; )
575 }
576
577 // non-const
578 else
579 {
580 // insert conversion
581 ALIB_DBG( auto& newCmd= )
582 prg.insertAt( prg.resultPC() + 1, ciAutoCast.Callback, false, 1,
583 ciAutoCast.TypeOrValue,
584 ciAutoCast.ReverseCastFunctionName, false,
585 idxInOriginal, idxInNormalized );
586
587 ++prg.resultPC();
588 ALIB_DBG( newCmd.DbgInfo.Callback= ciAutoCast.DbgCallbackName;
589 newCmd.DbgInfo.Plugin = pppAutoCast.plugin; )
590 }
591 }
592
593 break;
594
595 } // auto cast plug-in loop
596 } // pass loop
597
598 }
599 catch( Exception& e )
600 {
602 && !e.Type().IsEnumType<Exceptions>() )
605 throw;
606 }
607 catch( std::exception& stdException )
608 {
610 {
613 e.Add ( ALIB_CALLER_NULLED, Exceptions::StdExceptionInfo, stdException.what() );
614 throw e;
615 }
616 throw;
617 }
618
619
620 // not found
623 throw e;
624}
625
626
627void Program::AssembleBinaryOp( String& op, integer idxInOriginal, integer idxInNormalized )
628{
629 if( compileStorage == nullptr )
630 return;
632 ASSERT_ASSEMBLE
633
634 // If we have a global operator replacement, this will be used. However, in this case changes
635 // of it must not be passed back to the caller as long certain normalization flags are set.
636 String opReference= op;
637 bool aliased= false;
638 auto globalAliasIt= compiler.AlphabeticBinaryOperatorAliases.Find(op);
639 if( globalAliasIt != compiler.AlphabeticBinaryOperatorAliases.end() )
640 {
641 aliased= true;
642 opReference= globalAliasIt.Mapped();
643 }
644
645 bool foundOperator= false; // die Variable kann wohl raus. Stattdessen dort wo sie gesetzt wird ein "return" machen.
646 bool triedToAutoCast= false;
647
648 Box lhsOrigType= prg.at(prg.lhsResultPC()).ResultType;
649 Box rhsOrigType= prg.at(prg. resultPC()).ResultType;
650
651
652 for(;;)
653 {
654 collectArgs(2);
655 bool lhsIsConstant= prg.at(prg.lhsResultPC()).IsConstant() && !HasBits(compiler.CfgCompilation, Compilation::NoOptimization );
656 bool rhsIsConstant= prg.at(prg. resultPC()).IsConstant() && !HasBits(compiler.CfgCompilation, Compilation::NoOptimization );
657
659 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
660 opReference, lhsIsConstant, rhsIsConstant );
661
662 try
663 {
664 for( auto& ppp : compiler.plugins )
665 {
666 if( !ppp.plugin->TryCompilation( cInfo ) )
667 continue;
668
670 op= opReference;
671 else
673 op= globalAliasIt->first;
674
675
676 // --- identity? (like "a * 1" or "x && true") ---
677 if( cInfo.NonConstArgIsResult )
678 {
680
681 // lhs or rhs to remove?
682 if( lhsIsConstant )
683 prg.erase( prg.lhsResultStartPC() );
684 else
685 prg.eraseLast();
686
687 prg.popResultPC();
688 prg.resultPC()= prg.actPC();
689
690 foundOperator= true;
691 break;
692 }
693
694 // --- constant? ---
695 if( cInfo.Callback == nullptr )
696 {
698
699 // remove lhs, rhs and correct result stack
700 prg.erase( prg.lhsResultStartPC(), prg.resultPC() );
701
702 prg.popResultPC();
703 prg.resultPC()= prg.actPC();
704 prg.act()= VM::Command( cInfo.TypeOrValue, true, idxInOriginal, idxInNormalized );
705
706 foundOperator= true;
707 break;
708 }
709
710 //--- Callback ---
711
712 // found the correct result type stack
713 prg.popResultPC();
714 prg.add( cInfo.Callback, false, 2, cInfo.TypeOrValue, op, true, idxInOriginal, idxInNormalized );
715 prg.resultPC()= prg.actPC();
716
717 DBG_SET_CALLBACK_INFO
718
719 // done!
720 foundOperator= true;
721 break;
722 }
723
724 // success
725 if( foundOperator )
726 return;
727
728 if( !foundOperator && triedToAutoCast )
729 {
731 op,
732 compiler.TypeName( lhsOrigType ),
733 compiler.TypeName( rhsOrigType ) );
734
735 throw e;
736 }
737
738 // try auto cast (we do this even if types are equal )
739 triedToAutoCast= true;
741 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
742 op,
743 prg.at( prg.lhsResultPC() ).IsConstant(),
744 prg.at( prg. resultPC() ).IsConstant() );
745
746 for( auto& pppAutoCast : compiler.plugins )
747 {
748 if( !pppAutoCast.plugin->TryCompilation( ciAutoCast ) )
749 continue;
750
751 // cast for lhs?
752 if( !ciAutoCast.TypeOrValue.IsType<void>() )
753 {
754 // const?
755 if( ciAutoCast.Callback == nullptr )
756 {
757 // change constant value conversion
758 VM::Command& cmdToPatch= prg.at( prg.lhsResultPC() );
759 cmdToPatch.ResultType= ciAutoCast.TypeOrValue;
760 ALIB_DBG( cmdToPatch.DbgInfo.Plugin= pppAutoCast.plugin; )
761 }
762
763 // cast function upgrade for lhs?
764 else
765 {
766 // insert conversion
767 ALIB_DBG( auto& newCmd= )
768 prg.insertAt( prg.lhsResultPC() + 1, ciAutoCast.Callback, false, 1,
769 ciAutoCast.TypeOrValue,
770 ciAutoCast.ReverseCastFunctionName, false,
771 idxInOriginal, idxInNormalized );
772
773 ++prg.lhsResultPC();
774 ++prg.resultPC();
775 ALIB_DBG( newCmd.DbgInfo.Callback= ciAutoCast.DbgCallbackName;
776 newCmd.DbgInfo.Plugin= pppAutoCast.plugin; )
777 }
778 }
779
780 // cast for rhs?
781 if( !ciAutoCast.TypeOrValueRhs.IsType<void>() )
782 {
783 // const?
784 if( ciAutoCast.CallbackRhs == nullptr )
785 {
786 // change constant value conversion
787 prg.act().ResultType= ciAutoCast.TypeOrValueRhs;
788 ALIB_DBG( prg.act().DbgInfo.Plugin= pppAutoCast.plugin; )
789 }
790
791 // cast function upgrade for rhs?
792 else
793 {
794 // insert conversion
795 ALIB_DBG( auto& newCmd= )
796 prg.insertAt( prg.resultPC() + 1, ciAutoCast.CallbackRhs, false, 1,
797 ciAutoCast.TypeOrValueRhs,
798 ciAutoCast.ReverseCastFunctionNameRhs, false,
799 idxInOriginal, idxInNormalized );
800 ++prg.resultPC();
801
802 ALIB_DBG( newCmd.DbgInfo.Callback= ciAutoCast.DbgCallbackNameRhs;
803 newCmd.DbgInfo.Plugin= pppAutoCast.plugin; )
804 }
805 }
806 break;
807
808 } // auto cast plug-in loop
809 }
810 catch( Exception& e )
811 {
813 && !e.Type().IsEnumType<Exceptions>() )
816 throw;
817 }
818 catch( std::exception& stdException )
819 {
821 {
824 e.Add ( ALIB_CALLER_NULLED, Exceptions::StdExceptionInfo , stdException.what() );
825 throw e;
826 }
827 throw;
828 }
829
830 } // formally endless loop (max 2)
831}
832
833
834void Program::AssembleCondFinalize_Q( integer idxInOriginal, integer idxInNormalized )
835{
836 if( compileStorage == nullptr )
837 return;
839 ASSERT_ASSEMBLE
840
841 // Note:
842 // The "conditional stack" of tuples stores for each nested condition, three values:
843 // 1. The position of the lhs result,
844 // 2. The position of the jump command between T and F
845 // 3. An integer with two bits: bit 1 tells us whether Q was constant and bit 0 which value
846 // the constant Q had. "Had" because it is removed right away.
847
848 // Q constant?
849 int constQ= 0;
850 if( prg.act().IsConstant() && !HasBits(compiler.CfgCompilation, Compilation::NoOptimization ) )
851 {
853
854 Box& condition= prg.act().ResultType;
855 constQ= 2 + ( condition.Call<FIsTrue>() ? 1 : 0 );
856 prg.eraseLast(); // remove constant Q
857 }
858
859 // insert Q-Jump
860 prg.add( idxInOriginal, idxInNormalized, Command::JumpType::Conditional );
861 compileStorage->ConditionalStack.emplace_back( prg.actPC(), 0, constQ );
862}
863
864
865void Program::AssembleCondFinalize_T( integer idxInOriginal, integer idxInNormalized )
866{
867 if( compileStorage == nullptr )
868 return;
869
871 ASSERT_ASSEMBLE
872
873 // insert T-Jump
874 prg.add( idxInOriginal, idxInNormalized, Command::JumpType::Unconditional );
875 ++prg.resultPC(); // for the time being this points to the jump command.
876 // Otherwise upcoming F optimizations don't know where to find the start of F!
877
878 auto& actCond= compileStorage->ConditionalStack.back();
879
880 // patch Q-Jump to command after T-Jump
881 prg.at(actCond.QJumpPos).Parameter.Distance= prg.length() - actCond.QJumpPos;
882
883 // store T-Jump address on conditional stack
884 actCond.TJumpPos= prg.actPC();
885}
886
887void Program::AssembleCondFinalize_F( integer idxInOriginal, integer idxInNormalized )
888{
889 if( compileStorage == nullptr )
890 return;
891
893 ASSERT_ASSEMBLE
894
895 auto& actCond= compileStorage->ConditionalStack.back();
896
897 // patch result stack position of T one back (in Finalize_T we had increased it by one to
898 // point to the jump command, to protect it from being deleted with an lhs-delete
899 --prg.lhsResultPC();
900
901 prg.at( actCond.TJumpPos ).Parameter.Distance= prg.length() - actCond.TJumpPos;
902
903
904 // needs type alignment?
905 if( !prg.at( prg.lhsResultPC() ).ResultType.IsSameType( prg.at( prg.resultPC() ).ResultType ) )
906 {
907 collectArgs(2);
908 String condOp= A_CHAR("Q?T:F");
910 compileStorage->ConditionalStack.get_allocator().GetAllocator(),
911 condOp,
912 prg.at( prg.lhsResultPC() ).IsConstant(),
913 prg.at( prg. resultPC() ).IsConstant() );
914 bool found= false;
915 try
916 {
917 for( auto& ppp : compiler.plugins )
918 if( ppp.plugin->TryCompilation( ciAutoCast ) )
919 {
920 if( !ciAutoCast.TypeOrValue.IsType<void>() )
921 {
922 // const cast upgrade for T?
923 if( ciAutoCast.Callback == nullptr )
924 {
925 // change constant value conversion and patch type in jump command
926 prg.at(prg.lhsResultPC()).ResultType = ciAutoCast.TypeOrValue;
927
928 ALIB_DBG( prg.at(prg.lhsResultPC()).DbgInfo.Plugin= ppp.plugin; )
929 }
930
931 // upgrade function for T?
932 else if( ciAutoCast.Callback )
933 {
934 // jump one more (the other as well)
935 ++prg.at(actCond.QJumpPos).Parameter.Distance;
936 ++prg.at(actCond.TJumpPos).Parameter.Distance;
937
938 // insert conversion
939 ALIB_DBG( auto& newCmd= )
940 prg.insertAt( actCond.TJumpPos++, ciAutoCast.Callback, false, 1,
941 ciAutoCast.TypeOrValue,
942 ciAutoCast.ReverseCastFunctionName,
943 false,
944 idxInOriginal, idxInNormalized );
945 ALIB_DBG( newCmd.DbgInfo.Callback= ciAutoCast.DbgCallbackName;
946 newCmd.DbgInfo.Plugin= ppp.plugin; )
947 ++prg.lhsResultPC();
948 }
949 }
950
951 // const cast upgrade for F?
952 if( !ciAutoCast.TypeOrValueRhs.IsType<void>() )
953 {
954 if( ciAutoCast.Callback == nullptr )
955 {
956 // change constant value
957 prg.act().ResultType = ciAutoCast.TypeOrValueRhs;
958
959 ALIB_DBG( prg.act().DbgInfo.Callback= ciAutoCast.DbgCallbackNameRhs;
960 prg.act().DbgInfo.Plugin= ppp.plugin; )
961 }
962
963 // upgrade function for T?
964 else
965 {
966 // insert conversion
967 prg.add( ciAutoCast.CallbackRhs, false, 1,
968 ciAutoCast.TypeOrValueRhs,
969 ciAutoCast.ReverseCastFunctionNameRhs, false,
970 idxInOriginal, idxInNormalized );
971 ++prg.resultPC();
972 ++prg.at(actCond.TJumpPos).Parameter.Distance;
973
974 ALIB_DBG( prg.act().DbgInfo.Callback= ciAutoCast.DbgCallbackNameRhs;
975 prg.act().DbgInfo.Plugin= ppp.plugin; )
976 }
977 }
978
979 found= true;
980 break;
981 }
982 }
983 catch( Exception& e )
984 {
986 && !e.Type().IsEnumType<Exceptions>() )
989 throw;
990 }
991 catch( std::exception& stdException )
992 {
994 {
997 e.Add ( ALIB_CALLER_NULLED, Exceptions::StdExceptionInfo, stdException.what() );
998 throw e;
999 }
1000 throw;
1001 }
1002
1003
1004 if(!found)
1005 {
1007 compiler.TypeName( * ciAutoCast.ArgsBegin ),
1008 compiler.TypeName( *(ciAutoCast.ArgsBegin+1)) );
1010
1011 throw e;
1012 }
1013 }
1014
1015 // was this a constant conditional to be optimized out?
1016 if( actCond.ConstFlags )
1017 {
1018 // eliminate T?
1019 if( (actCond.ConstFlags & 1) == 0 )
1020 {
1021 prg.erase( actCond.QJumpPos, actCond.TJumpPos + 1 );
1022 }
1023
1024 // eliminate F?
1025 else
1026 {
1027 prg.erase( actCond.TJumpPos, prg.actPC() + 1 );
1028 prg.erase( actCond.QJumpPos );
1029 }
1030 }
1031 else
1032 // mark last command as part of conditional. Otherwise constant F-terms become optimized
1033 prg.act().SetEndOfConditionalFlag();
1034
1035
1036 // clean the conditional stack
1037 compileStorage->ConditionalStack.pop_back();
1038
1039 // remove results Q [? T : F]
1040 prg.popResultPC();
1041 prg.popResultPC();
1042 prg.resultPC()= prg.actPC();
1043}
1044
1045
1047{
1048 if( compileStorage == nullptr )
1049 return;
1050
1052 ASSERT_ASSEMBLE
1053
1055 "Finalizing program, while conditional stack is of size {}.",
1057 ALIB_ASSERT_ERROR( compileStorage->ResultStack.size() == 1, "EXPR",
1058 "Finalizing program, while result stack is of size {}.",
1059 compileStorage->ResultStack.size() )
1060
1061
1062 // copy the program from the temporary vector to a simple array, allocated with the
1063 // compile-time scope's allocator.
1065 commandsCount= static_cast<integer>( compileStorage->Assembly.size() );
1067 auto* cmd= commands;
1068 for( auto* it : compileStorage->Assembly )
1069 new ( cmd++ ) VM::Command(*it);
1070
1071 compileStorage= nullptr;
1073}
1074
1075#undef ASSERT_ASSEMBLE
1076#undef DBG_SET_CALLBACK_INFO
1077
1078}}} // namespace [alib::expressions::detail]
1079
bool IsType() const
decltype(std::declval< typename TFDecl::Signature >()(std::declval< Box & >(), std::declval< TArgs >()...)) Call(TArgs &&... args) const
Definition box.inl:1175
const TUnboxable Unbox() const
HashMap< MonoAllocator, String, String, alib::hash_string_ignore_case< character >, alib::equal_to_string_ignore_case< character > > AlphabeticUnaryOperatorAliases
Definition compiler.hpp:132
HashMap< MonoAllocator, String, String, alib::hash_string_ignore_case< character >, alib::equal_to_string_ignore_case< character > > AlphabeticBinaryOperatorAliases
Definition compiler.hpp:142
ALIB_API NString TypeName(Type box)
Definition compiler.cpp:434
Compilation CfgCompilation
Compilation flags.
Definition compiler.hpp:273
strings::util::Token CfgNestedExpressionFunction
Definition compiler.hpp:264
Normalization CfgNormalization
Definition compiler.hpp:277
ALIB_API void WriteFunctionSignature(Box **boxArray, size_t qty, AString &target)
Definition compiler.cpp:450
virtual ALIB_API Expression GetNamed(const String &name)
Definition compiler.cpp:392
ALIB_API void AssembleCondFinalize_T(integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:865
ALIB_API ~Program()
Destructor.
Definition program.cpp:43
ALIB_API bool collectArgs(integer qty)
Definition program.cpp:214
ALIB_API void AssembleCondFinalize_F(integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:887
ALIB_API void AssembleUnaryOp(String &op, integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:443
integer commandsCount
The number of commands.
Definition program.hpp:56
Compiler & compiler
The compiler that created this object.
Definition program.hpp:39
int qtyOptimizations
Counter of the number of optimization made during program assembly.
Definition program.hpp:63
ALIB_API Program(Compiler &pCompiler, ExpressionVal &pExpression, MonoAllocator *ctAlloc)
Definition program.cpp:34
ALIB_API void AssembleConstant(Box &value, integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:237
ALIB_API void AssembleCondFinalize_Q(integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:834
StdVectorMono< Expression > ctNestedExpressions
Definition program.hpp:60
VM::Command * commands
The array of commands.
Definition program.hpp:53
ALIB_API void AssembleFunction(AString &functionName, bool isIdentifier, int qtyArgs, integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:251
ExpressionVal & expression
The expression that this program evaluates.
Definition program.hpp:42
ALIB_API void AssembleBinaryOp(String &op, integer idxInOriginal, integer idxInNormalized)
Definition program.cpp:627
DbgInformation DbgInfo
Operation code of this command. Available only with debug-builds.
ALIB_API const Enum & Type() const
Exception & Add(const lang::CallerInfo &ci, TEnum type, TArgs &&... args)
std::vector< Slot > plugins
The plug-ins we have attached in descending priority order.
void DbgDisableBufferReplacementWarning()
Definition tastring.inl:363
constexpr bool IsNotEmpty() const
Definition string.hpp:389
const String & GetDefinitionName() const
Definition token.hpp:305
ALIB_API bool Match(const String &needle)
Definition token.cpp:314
#define ALIB_CALLER_NULLED
Definition alib.hpp:1173
#define A_CHAR(STR)
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_FORCE_INLINE
Definition alib.hpp:650
#define ALIB_ERROR(...)
Definition alib.hpp:1267
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
#define ALIB_DBG(...)
Definition alib.hpp:390
@ ReplaceVerbalOperatorsToDefinedLetterCase
See sibling flag ReplaceVerbalOperatorsToSymbolic.
@ UnknownIdentifier
Compile-time exception thrown when an expression uses an unknown identifier name.
@ UnknownFunction
Compile-time exception thrown when an expression uses an unknown function name.
@ NamedExpressionNotFound
Compile-time exception thrown when an expression refers to an unknown named nested expression.
static ALIB_FORCE_INLINE void Destruct(T &object)
Definition alib.cpp:69
lang::Exception Exception
Type alias in namespace alib.
std::vector< T, SCAMono< T > > StdVectorMono
Type alias in namespace alib.
Definition stdvector.hpp:21
boxing::Box Box
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
integer Integral() const
Definition enum.hpp:119
bool IsEnumType() const
bool NonConstArgIsResult
Output: Used with optimization, see this struct's documentation for more information.
List< MonoAllocator, String > & FunctionsWithNonMatchingArguments
CallbackDecl Callback
Output: The native C++ callback function to be set by one of the plug-ins.
StdVectorMono< Box > * Stack
Definition scope.hpp:140
List< MonoAllocator, String > FunctionsWithNonMatchingArguments
Definition program.hpp:102
StdVectorMono< ConditionalInfo > ConditionalStack
Definition program.hpp:98
StdVectorMono< VirtualMachine::Command * > Assembly
Definition program.hpp:73
CompilerPlugin * Plugin
The plug-in that provided the callback or constant.