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