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