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